Merge "Make java_sdk_library dependencies explicit" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 6f8a189..bd17d6d 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -20,6 +20,7 @@
java_aconfig_libraries: [
// !!! KEEP THIS LIST ALPHABETICAL !!!
"aconfig_mediacodec_flags_java_lib",
+ "android-sdk-flags-java",
"android.adaptiveauth.flags-aconfig-java",
"android.app.appfunctions.flags-aconfig-java",
"android.app.contextualsearch.flags-aconfig-java",
@@ -1429,6 +1430,18 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.app.appfunctions.exported-flags-aconfig-java",
+ aconfig_declarations: "android.app.appfunctions.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+ min_sdk_version: "30",
+}
+
// Adaptive Auth
aconfig_declarations {
name: "android.adaptiveauth.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index f8907f3..258440f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -98,6 +98,7 @@
":android.frameworks.location.altitude-V2-java-source",
":android.hardware.biometrics.common-V4-java-source",
":android.hardware.biometrics.fingerprint-V5-java-source",
+ ":android.hardware.biometrics.fingerprint.virtualhal-java-source",
":android.hardware.biometrics.face-V4-java-source",
":android.hardware.gnss-V2-java-source",
":android.hardware.graphics.common-V3-java-source",
diff --git a/BROADCASTS_OWNERS b/BROADCASTS_OWNERS
index 01f1f8a..f0cbe46 100644
--- a/BROADCASTS_OWNERS
+++ b/BROADCASTS_OWNERS
@@ -1,5 +1,5 @@
# Bug component: 316181
[email protected]
[email protected]
+set noparent
+
[email protected]
[email protected] #{LAST_RESORT_SUGGESTION}
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 5f32ba0..ec58210 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -50,7 +50,7 @@
framework_minus_apex_cmd = "$(location hoststubgen) " +
"@$(location :ravenwood-standard-options) " +
"--debug-log $(location hoststubgen_framework-minus-apex.log) " +
- "--out-impl-jar $(location ravenwood.jar) " +
+ "--out-jar $(location ravenwood.jar) " +
"--in-jar $(location :framework-minus-apex-for-hoststubgen) " +
"--policy-override-file $(location :ravenwood-framework-policies) " +
"--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) "
@@ -183,7 +183,7 @@
"--stats-file $(location hoststubgen_services.core_stats.csv) " +
"--supported-api-list-file $(location hoststubgen_services.core_apis.csv) " +
- "--out-impl-jar $(location ravenwood.jar) " +
+ "--out-jar $(location ravenwood.jar) " +
"--gen-keep-all-file $(location hoststubgen_services.core_keep_all.txt) " +
"--gen-input-dump-file $(location hoststubgen_services.core_dump.txt) " +
diff --git a/android-sdk-flags/Android.bp b/android-sdk-flags/Android.bp
new file mode 100644
index 0000000..79a0b9a
--- /dev/null
+++ b/android-sdk-flags/Android.bp
@@ -0,0 +1,30 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+aconfig_declarations {
+ name: "android-sdk-flags",
+ package: "android.sdk",
+ container: "system",
+ srcs: ["flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android-sdk-flags-java",
+ aconfig_declarations: "android-sdk-flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/android-sdk-flags/flags.aconfig b/android-sdk-flags/flags.aconfig
new file mode 100644
index 0000000..cfe298e
--- /dev/null
+++ b/android-sdk-flags/flags.aconfig
@@ -0,0 +1,12 @@
+package: "android.sdk"
+container: "system"
+
+flag {
+ name: "major_minor_versioning_scheme"
+ namespace: "android_sdk"
+ description: "Use the new SDK major.minor versioning scheme (e.g. Android 40.1) which replaces the old single-integer scheme (e.g. Android 15)."
+ bug: "350458259"
+
+ # Use is_fixed_read_only because DeviceConfig may not be available when Build.VERSION_CODES is first accessed
+ is_fixed_read_only: true
+}
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/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
index f20b170..3577fcd 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -194,7 +194,7 @@
/**
* Simple benchmark for the amount of time to send a given number of messages
*/
- @Test
+ // @Test Temporarily disabled
@Parameters(method = "getParams")
public void time(Config config) throws Exception {
reset();
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
index af3c405..ac57100 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -198,7 +198,7 @@
executor.awaitTermination(5, TimeUnit.SECONDS);
}
- @Test
+ // @Test Temporarily disabled
@Parameters(method = "getParams")
public void throughput(Config config) throws Exception {
setup(config);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
index 4cf46e5..f01ac02 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
@@ -565,7 +565,6 @@
}
@Test
- @Parameters(method = "getData")
public void time_new_byteArray() throws Exception {
final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
@@ -574,7 +573,6 @@
}
@Test
- @Parameters(method = "getData")
public void time_ByteBuffer_allocate() throws Exception {
final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
diff --git a/apct-tests/perftests/protolog/Android.bp b/apct-tests/perftests/tracing/Android.bp
similarity index 100%
rename from apct-tests/perftests/protolog/Android.bp
rename to apct-tests/perftests/tracing/Android.bp
diff --git a/apct-tests/perftests/protolog/AndroidManifest.xml b/apct-tests/perftests/tracing/AndroidManifest.xml
similarity index 100%
rename from apct-tests/perftests/protolog/AndroidManifest.xml
rename to apct-tests/perftests/tracing/AndroidManifest.xml
diff --git a/apct-tests/perftests/protolog/AndroidTest.xml b/apct-tests/perftests/tracing/AndroidTest.xml
similarity index 100%
rename from apct-tests/perftests/protolog/AndroidTest.xml
rename to apct-tests/perftests/tracing/AndroidTest.xml
diff --git a/apct-tests/perftests/protolog/OWNERS b/apct-tests/perftests/tracing/OWNERS
similarity index 100%
rename from apct-tests/perftests/protolog/OWNERS
rename to apct-tests/perftests/tracing/OWNERS
diff --git a/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtoLogPerfTest.java b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java
similarity index 100%
rename from apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtoLogPerfTest.java
rename to apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java
diff --git a/apex/blobstore/OWNERS b/apex/blobstore/OWNERS
index a53bbea..676cbc7 100644
--- a/apex/blobstore/OWNERS
+++ b/apex/blobstore/OWNERS
@@ -1,2 +1,5 @@
+# Bug component: 25692
+set noparent
+
[email protected]
[email protected]
[email protected] #{LAST_RESORT_SUGGESTION}
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index 80db264..5f55075 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -23,3 +23,10 @@
description: "Introduce a new RUN_BACKUP_JOBS permission and exemption logic allowing for longer running jobs for apps whose primary purpose is to backup or sync content."
bug: "318731461"
}
+
+flag {
+ name: "cleanup_empty_jobs"
+ namespace: "backstage_power"
+ description: "Enables automatic cancellation of jobs due to leaked JobParameters, reducing unnecessary battery drain and improving system efficiency. This includes logging and traces for better issue diagnosis."
+ bug: "349688611"
+}
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
index 96494ec..11d17ca 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
@@ -85,6 +85,14 @@
*/
@UnsupportedAppUsage
void jobFinished(int jobId, boolean reschedule);
+
+ /*
+ * Inform JobScheduler to force finish this job because the client has lost
+ * the job handle. jobFinished can no longer be called from the client.
+ * @param jobId Unique integer used to identify this job
+ */
+ void forceJobFinished(int jobId);
+
/*
* Inform JobScheduler of a change in the estimated transfer payload.
*
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index e833bb9..52a761f 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -34,15 +34,21 @@
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;
+import android.system.SystemCleaner;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.Cleaner;
/**
* Contains the parameters used to configure/identify your job. You do not create this object
* yourself, instead it is handed in to your application by the System.
*/
public class JobParameters implements Parcelable {
+ private static final String TAG = "JobParameters";
/** @hide */
public static final int INTERNAL_STOP_REASON_UNKNOWN = -1;
@@ -306,6 +312,10 @@
private int mStopReason = STOP_REASON_UNDEFINED;
private int mInternalStopReason = INTERNAL_STOP_REASON_UNKNOWN;
private String debugStopReason; // Human readable stop reason for debugging.
+ @Nullable
+ private JobCleanupCallback mJobCleanupCallback;
+ @Nullable
+ private Cleaner.Cleanable mCleanable;
/** @hide */
public JobParameters(IBinder callback, String namespace, int jobId, PersistableBundle extras,
@@ -326,6 +336,8 @@
this.mTriggeredContentAuthorities = triggeredContentAuthorities;
this.mNetwork = network;
this.mJobNamespace = namespace;
+ this.mJobCleanupCallback = null;
+ this.mCleanable = null;
}
/**
@@ -597,6 +609,8 @@
mStopReason = in.readInt();
mInternalStopReason = in.readInt();
debugStopReason = in.readString();
+ mJobCleanupCallback = null;
+ mCleanable = null;
}
/** @hide */
@@ -612,6 +626,54 @@
this.debugStopReason = debugStopReason;
}
+ /** @hide */
+ public void initCleaner(JobCleanupCallback jobCleanupCallback) {
+ mJobCleanupCallback = jobCleanupCallback;
+ mCleanable = SystemCleaner.cleaner().register(this, mJobCleanupCallback);
+ }
+
+ /**
+ * Lazy initialize the cleaner and enable it
+ *
+ * @hide
+ */
+ public void enableCleaner() {
+ if (mJobCleanupCallback == null) {
+ initCleaner(new JobCleanupCallback(IJobCallback.Stub.asInterface(callback), jobId));
+ }
+ mJobCleanupCallback.enableCleaner();
+ }
+
+ /**
+ * Disable the cleaner from running and unregister it
+ *
+ * @hide
+ */
+ public void disableCleaner() {
+ if (mJobCleanupCallback != null) {
+ mJobCleanupCallback.disableCleaner();
+ if (mCleanable != null) {
+ mCleanable.clean();
+ mCleanable = null;
+ }
+ mJobCleanupCallback = null;
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ @Nullable
+ public Cleaner.Cleanable getCleanable() {
+ return mCleanable;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ @Nullable
+ public JobCleanupCallback getJobCleanupCallback() {
+ return mJobCleanupCallback;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -647,6 +709,67 @@
dest.writeString(debugStopReason);
}
+ /**
+ * JobCleanupCallback is used track JobParameters leak. If the job is started
+ * and jobFinish is not called at the time of garbage collection of JobParameters
+ * instance, it is considered a job leak. Force finish the job.
+ *
+ * @hide
+ */
+ public static class JobCleanupCallback implements Runnable {
+ private final IJobCallback mCallback;
+ private final int mJobId;
+ private boolean mIsCleanerEnabled;
+
+ public JobCleanupCallback(
+ IJobCallback callback,
+ int jobId) {
+ mCallback = callback;
+ mJobId = jobId;
+ mIsCleanerEnabled = false;
+ }
+
+ /**
+ * Check if the cleaner is enabled
+ *
+ * @hide
+ */
+ public boolean isCleanerEnabled() {
+ return mIsCleanerEnabled;
+ }
+
+ /**
+ * Enable the cleaner to detect JobParameter leak
+ *
+ * @hide
+ */
+ public void enableCleaner() {
+ mIsCleanerEnabled = true;
+ }
+
+ /**
+ * Disable the cleaner from running.
+ *
+ * @hide
+ */
+ public void disableCleaner() {
+ mIsCleanerEnabled = false;
+ }
+
+ /** @hide */
+ @Override
+ public void run() {
+ if (!isCleanerEnabled()) {
+ return;
+ }
+ try {
+ mCallback.forceJobFinished(mJobId);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Could not destroy running job", e);
+ }
+ }
+ }
+
public static final @android.annotation.NonNull Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
@Override
public JobParameters createFromParcel(Parcel in) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
index 79d87ed..5f80c52 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
@@ -165,7 +165,13 @@
case MSG_EXECUTE_JOB: {
final JobParameters params = (JobParameters) msg.obj;
try {
+ if (Flags.cleanupEmptyJobs()) {
+ params.enableCleaner();
+ }
boolean workOngoing = JobServiceEngine.this.onStartJob(params);
+ if (Flags.cleanupEmptyJobs() && !workOngoing) {
+ params.disableCleaner();
+ }
ackStartMessage(params, workOngoing);
} catch (Exception e) {
Log.e(TAG, "Error while executing job: " + params.getJobId());
@@ -190,6 +196,9 @@
IJobCallback callback = params.getCallback();
if (callback != null) {
try {
+ if (Flags.cleanupEmptyJobs()) {
+ params.disableCleaner();
+ }
callback.jobFinished(params.getJobId(), needsReschedule);
} catch (RemoteException e) {
Log.e(TAG, "Error reporting job finish to system: binder has gone" +
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 3d25ed5..97c6e25 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -5808,9 +5808,10 @@
static void dumpHelp(PrintWriter pw) {
pw.println("Job Scheduler (jobscheduler) dump options:");
- pw.println(" [-h] [package] ...");
+ pw.println(" [-h] [package] [--proto] ...");
pw.println(" -h: print this help");
pw.println(" [package] is an optional package name to limit the output to.");
+ pw.println(" --proto: output dump in protocol buffer format.");
}
/** Sort jobs by caller UID, then by Job ID. */
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index be8e304..ee246d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -129,6 +129,8 @@
private static final String[] VERB_STRINGS = {
"VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
};
+ private static final String TRACE_JOB_FORCE_FINISHED_PREFIX = "forceJobFinished:";
+ private static final String TRACE_JOB_FORCE_FINISHED_DELIMITER = "#";
// States that a job occupies while interacting with the client.
static final int VERB_BINDING = 0;
@@ -292,6 +294,11 @@
}
@Override
+ public void forceJobFinished(int jobId) {
+ doForceJobFinished(this, jobId);
+ }
+
+ @Override
public void updateEstimatedNetworkBytes(int jobId, JobWorkItem item,
long downloadBytes, long uploadBytes) {
doUpdateEstimatedNetworkBytes(this, jobId, item, downloadBytes, uploadBytes);
@@ -762,6 +769,35 @@
}
}
+ /**
+ * This method just adds traces to evaluate jobs that leak jobparameters at the client.
+ * It does not stop the job.
+ */
+ void doForceJobFinished(JobCallback cb, int jobId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final JobStatus executing;
+ synchronized (mLock) {
+ // not the current job, presumably it has finished in some way already
+ if (!verifyCallerLocked(cb)) {
+ return;
+ }
+
+ executing = getRunningJobLocked();
+ }
+ if (executing != null && jobId == executing.getJobId()) {
+ final StringBuilder stateSuffix = new StringBuilder();
+ stateSuffix.append(TRACE_JOB_FORCE_FINISHED_PREFIX);
+ stateSuffix.append(executing.getBatteryName());
+ stateSuffix.append(TRACE_JOB_FORCE_FINISHED_DELIMITER);
+ stateSuffix.append(executing.getJobId());
+ Trace.instant(Trace.TRACE_TAG_POWER, stateSuffix.toString());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void doAcknowledgeGetTransferredDownloadBytesMessage(JobCallback cb, int jobId,
int workId, @BytesLong long transferredBytes) {
// TODO(255393346): Make sure apps call this appropriately and monitor for abuse
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index 52670a2..dd0d1b6 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -10,14 +10,10 @@
]
},
{
- "name": "CtsBRSTestCases",
- "options": [
- {"exclude-annotation": "androidx.test.filters.FlakyTest"},
- {"exclude-annotation": "org.junit.Ignore"}
- ]
+ "name": "CtsBRSTestCases"
},
{
- "name": "FrameworksServicesTests_com_android_server_usage_Presubmit"
+ "name": "FrameworksServicesTests_com_android_server_usage"
}
],
"postsubmit": [
diff --git a/api/Android.bp b/api/Android.bp
index 341be3d53..533f9f6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -87,6 +87,7 @@
"framework-permission",
"framework-permission-s",
"framework-profiling",
+ "framework-photopicker",
"framework-scheduling",
"framework-sdkextensions",
"framework-statsd",
diff --git a/api/api.go b/api/api.go
index 5b7f534..e9f1fee 100644
--- a/api/api.go
+++ b/api/api.go
@@ -15,7 +15,7 @@
package api
import (
- "sort"
+ "slices"
"github.com/google/blueprint/proptools"
@@ -75,31 +75,25 @@
var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
-func (a *CombinedApis) bootclasspath(ctx android.ConfigAndErrorContext) []string {
- return a.properties.Bootclasspath.GetOrDefault(a.ConfigurableEvaluator(ctx), nil)
-}
-
-func (a *CombinedApis) systemServerClasspath(ctx android.ConfigAndErrorContext) []string {
- return a.properties.System_server_classpath.GetOrDefault(a.ConfigurableEvaluator(ctx), nil)
-}
-
func (a *CombinedApis) apiFingerprintStubDeps(ctx android.BottomUpMutatorContext) []string {
- ret := []string{}
+ bootClasspath := a.properties.Bootclasspath.GetOrDefault(ctx, nil)
+ systemServerClasspath := a.properties.System_server_classpath.GetOrDefault(ctx, nil)
+ var ret []string
ret = append(
ret,
- transformArray(a.bootclasspath(ctx), "", ".stubs")...,
+ transformArray(bootClasspath, "", ".stubs")...,
)
ret = append(
ret,
- transformArray(a.bootclasspath(ctx), "", ".stubs.system")...,
+ transformArray(bootClasspath, "", ".stubs.system")...,
)
ret = append(
ret,
- transformArray(a.bootclasspath(ctx), "", ".stubs.module_lib")...,
+ transformArray(bootClasspath, "", ".stubs.module_lib")...,
)
ret = append(
ret,
- transformArray(a.systemServerClasspath(ctx), "", ".stubs.system_server")...,
+ transformArray(systemServerClasspath, "", ".stubs.system_server")...,
)
return ret
}
@@ -129,7 +123,7 @@
Cmd *string
Dists []android.Dist
Out []string
- Srcs []string
+ Srcs proptools.Configurable[[]string]
Tools []string
Visibility []string
}
@@ -137,7 +131,7 @@
type libraryProps struct {
Name *string
Sdk_version *string
- Static_libs []string
+ Static_libs proptools.Configurable[[]string]
Visibility []string
Defaults []string
Is_stubs_module *bool
@@ -145,7 +139,7 @@
type fgProps struct {
Name *string
- Srcs []string
+ Srcs proptools.Configurable[[]string]
Visibility []string
}
@@ -166,7 +160,7 @@
// The module for the non-updatable / non-module part of the api.
BaseTxt string
// The list of modules that are relevant for this merged txt.
- Modules []string
+ Modules proptools.Configurable[[]string]
// The output tag for each module to use.e.g. {.public.api.txt} for current.txt
ModuleTag string
// public, system, module-lib or system-server
@@ -190,7 +184,8 @@
props.Tools = []string{"metalava"}
props.Out = []string{filename}
props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
- props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
+ props.Srcs = proptools.NewSimpleConfigurable([]string{txt.BaseTxt})
+ props.Srcs.Append(createSrcs(txt.Modules, txt.ModuleTag))
if doDist {
props.Dists = []android.Dist{
{
@@ -209,11 +204,11 @@
ctx.CreateModule(genrule.GenRuleFactory, &props)
}
-func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
+func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules proptools.Configurable[[]string]) {
for _, i := range []struct {
name string
tag string
- modules []string
+ modules proptools.Configurable[[]string]
}{
{
name: "all-modules-public-annotations",
@@ -240,33 +235,39 @@
}
}
-func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedPublicStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
+ transformConfigurableArray(modules, "", ".stubs")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs")
- props.Static_libs = transformArray(modules, "", ".stubs")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
+ transformConfigurableArray(modules, "", ".stubs.exportable")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs-exportable")
- props.Static_libs = transformArray(modules, "", ".stubs.exportable")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedSystemStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
// First create the all-updatable-modules-system-stubs
{
- updatable_modules := removeAll(modules, non_updatable_modules)
+ updatable_modules := modules.Clone()
+ removeAll(updatable_modules, non_updatable_modules)
+ transformConfigurableArray(updatable_modules, "", ".stubs.system")
props := libraryProps{}
props.Name = proptools.StringPtr("all-updatable-modules-system-stubs")
- props.Static_libs = transformArray(updatable_modules, "", ".stubs.system")
+ props.Static_libs = updatable_modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -275,10 +276,11 @@
// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
// into all-modules-system-stubs.
{
+ static_libs := transformArray(non_updatable_modules, "", ".stubs.system")
+ static_libs = append(static_libs, "all-updatable-modules-system-stubs")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-system-stubs")
- props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.system")
- props.Static_libs = append(props.Static_libs, "all-updatable-modules-system-stubs")
+ props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -286,13 +288,15 @@
}
}
-func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
// First create the all-updatable-modules-system-stubs
{
- updatable_modules := removeAll(modules, non_updatable_modules)
+ updatable_modules := modules.Clone()
+ removeAll(updatable_modules, non_updatable_modules)
+ transformConfigurableArray(updatable_modules, "", ".stubs.exportable.system")
props := libraryProps{}
props.Name = proptools.StringPtr("all-updatable-modules-system-stubs-exportable")
- props.Static_libs = transformArray(updatable_modules, "", ".stubs.exportable.system")
+ props.Static_libs = updatable_modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -301,10 +305,11 @@
// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
// into all-modules-system-stubs.
{
+ static_libs := transformArray(non_updatable_modules, "", ".stubs.exportable.system")
+ static_libs = append(static_libs, "all-updatable-modules-system-stubs-exportable")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-system-stubs-exportable")
- props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.exportable.system")
- props.Static_libs = append(props.Static_libs, "all-updatable-modules-system-stubs-exportable")
+ props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -315,7 +320,7 @@
func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs")
- props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.test")
+ props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.test"))
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -325,25 +330,27 @@
func createMergedTestExportableStubsForNonUpdatableModules(ctx android.LoadHookContext) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs-exportable")
- props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.exportable.test")
+ props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.exportable.test"))
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
+func createMergedFrameworkImpl(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
// This module is for the "framework-all" module, which should not include the core libraries.
- modules = removeAll(modules, core_libraries_modules)
+ removeAll(modules, core_libraries_modules)
// Remove the modules that belong to non-updatable APEXes since those are allowed to compile
// against unstable APIs.
- modules = removeAll(modules, non_updatable_modules)
+ removeAll(modules, non_updatable_modules)
// First create updatable-framework-module-impl, which contains all updatable modules.
// This module compiles against module_lib SDK.
{
+ transformConfigurableArray(modules, "", ".impl")
props := libraryProps{}
props.Name = proptools.StringPtr("updatable-framework-module-impl")
- props.Static_libs = transformArray(modules, "", ".impl")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
@@ -352,65 +359,74 @@
// Now create all-framework-module-impl, which contains updatable-framework-module-impl
// and all non-updatable modules. This module compiles against hidden APIs.
{
+ static_libs := transformArray(non_updatable_modules, "", ".impl")
+ static_libs = append(static_libs, "updatable-framework-module-impl")
props := libraryProps{}
props.Name = proptools.StringPtr("all-framework-module-impl")
- props.Static_libs = transformArray(non_updatable_modules, "", ".impl")
- props.Static_libs = append(props.Static_libs, "updatable-framework-module-impl")
+ props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("core_platform")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
}
}
-func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
// The user of this module compiles against the "core" SDK and against non-updatable modules,
// so remove to avoid dupes.
- modules = removeAll(modules, core_libraries_modules)
- modules = removeAll(modules, non_updatable_modules)
+ removeAll(modules, core_libraries_modules)
+ removeAll(modules, non_updatable_modules)
+ transformConfigurableArray(modules, "", ".stubs.exportable.module_lib")
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api-exportable")
- props.Static_libs = transformArray(modules, "", ".stubs.exportable.module_lib")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
// The user of this module compiles against the "core" SDK and against non-updatable modules,
// so remove to avoid dupes.
- modules = removeAll(modules, core_libraries_modules)
- modules = removeAll(modules, non_updatable_modules)
+ removeAll(modules, core_libraries_modules)
+ removeAll(modules, non_updatable_modules)
+ transformConfigurableArray(modules, "", ".stubs.module_lib")
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
- props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
+func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContext, bootclasspath, system_server_classpath proptools.Configurable[[]string]) {
// The user of this module compiles against the "core" SDK and against non-updatable bootclasspathModules,
// so remove to avoid dupes.
- bootclasspathModules := removeAll(bootclasspath, core_libraries_modules)
- bootclasspathModules = removeAll(bootclasspath, non_updatable_modules)
- modules := append(
- // Include all the module-lib APIs from the bootclasspath libraries.
- transformArray(bootclasspathModules, "", ".stubs.exportable.module_lib"),
- // Then add all the system-server APIs from the service-* libraries.
- transformArray(system_server_classpath, "", ".stubs.exportable.system_server")...,
- )
+ bootclasspathModules := bootclasspath.Clone()
+ removeAll(bootclasspathModules, core_libraries_modules)
+ removeAll(bootclasspathModules, non_updatable_modules)
+ transformConfigurableArray(bootclasspathModules, "", ".stubs.exportable.module_lib")
+
+ system_server_classpath = system_server_classpath.Clone()
+ transformConfigurableArray(system_server_classpath, "", ".stubs.exportable.system_server")
+
+ // Include all the module-lib APIs from the bootclasspath libraries.
+ // Then add all the system-server APIs from the service-* libraries.
+ bootclasspathModules.Append(system_server_classpath)
+
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-system_server_api-exportable")
- props.Static_libs = modules
+ props.Static_libs = bootclasspathModules
props.Sdk_version = proptools.StringPtr("system_server_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []string) {
+func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
props := fgProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs-source")
props.Srcs = createSrcs(modules, "{.public.stubs.source}")
@@ -418,7 +434,14 @@
ctx.CreateModule(android.FileGroupFactory, &props)
}
-func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string, baseTxtModulePrefix, stubsTypeSuffix string, doDist bool) {
+func createMergedTxts(
+ ctx android.LoadHookContext,
+ bootclasspath proptools.Configurable[[]string],
+ system_server_classpath proptools.Configurable[[]string],
+ baseTxtModulePrefix string,
+ stubsTypeSuffix string,
+ doDist bool,
+) {
var textFiles []MergedTxtDefinition
tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
@@ -463,11 +486,10 @@
}
func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
- bootclasspath := a.bootclasspath(ctx)
- system_server_classpath := a.systemServerClasspath(ctx)
+ bootclasspath := a.properties.Bootclasspath.Clone()
+ system_server_classpath := a.properties.System_server_classpath.Clone()
if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
- bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
- sort.Strings(bootclasspath)
+ bootclasspath.AppendSimpleValue(a.properties.Conditional_bootclasspath)
}
createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false)
createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-exportable-", "-exportable-", true)
@@ -500,8 +522,10 @@
// Various utility methods below.
// Creates an array of ":<m><tag>" for each m in <modules>.
-func createSrcs(modules []string, tag string) []string {
- return transformArray(modules, ":", tag)
+func createSrcs(modules proptools.Configurable[[]string], tag string) proptools.Configurable[[]string] {
+ result := modules.Clone()
+ transformConfigurableArray(result, ":", tag)
+ return result
}
// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
@@ -513,11 +537,23 @@
return a
}
-func removeAll(s []string, vs []string) []string {
- for _, v := range vs {
- s = remove(s, v)
- }
- return s
+// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
+func transformConfigurableArray(modules proptools.Configurable[[]string], prefix, suffix string) {
+ modules.AddPostProcessor(func(s []string) []string {
+ return transformArray(s, prefix, suffix)
+ })
+}
+
+func removeAll(s proptools.Configurable[[]string], vs []string) {
+ s.AddPostProcessor(func(s []string) []string {
+ a := make([]string, 0, len(s))
+ for _, module := range s {
+ if !slices.Contains(vs, module) {
+ a = append(a, module)
+ }
+ }
+ return a
+ })
}
func remove(s []string, v string) []string {
diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt
index adcc3df..70e5a68 100644
--- a/boot/hiddenapi/hiddenapi-unsupported.txt
+++ b/boot/hiddenapi/hiddenapi-unsupported.txt
@@ -183,7 +183,6 @@
Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
Landroid/view/IDockedStackListener$Stub;-><init>()V
-Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
Landroid/view/IRotationWatcher$Stub;-><init>()V
Landroid/view/IWindow$Stub;-><init>()V
diff --git a/cmds/uinput/examples/test-touchpad.evemu b/cmds/uinput/examples/test-touchpad.evemu
new file mode 100644
index 0000000..34ee572
--- /dev/null
+++ b/cmds/uinput/examples/test-touchpad.evemu
@@ -0,0 +1,44 @@
+# EVEMU 1.2
+# This is an evemu "recording" of an Apple Magic Trackpad (1st generation), but
+# that doesn't actually make any movements. It just runs for a very long time,
+# to make Android think a touchpad is connected. This is useful for testing
+# things like the settings in System > Touchpad, which only appear when one is
+# connected.
+#
+# It can be played by piping it to the uinput command over ADB:
+# $ adb shell uinput - < test-touchpad.evemu
+N: Fake touchpad
+I: 0005 05ac 030e 0160
+P: 05 00 00 00 00 00 00 00
+B: 00 0b 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 01 00 00 00 00 00
+B: 01 20 e5 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 02 00 00 00 00 00 00 00 00
+B: 03 03 00 00 00 00 80 73 02
+B: 04 10 00 00 00 00 00 00 00
+B: 05 00 00 00 00 00 00 00 00
+B: 11 00 00 00 00 00 00 00 00
+B: 12 00 00 00 00 00 00 00 00
+A: 00 -2909 3167 4 0 46
+A: 01 -2456 2565 4 0 45
+A: 2f 0 15 0 0 0
+A: 30 0 1020 4 0 0
+A: 31 0 1020 4 0 0
+A: 34 -31 32 1 0 0
+A: 35 -2909 3167 4 0 46
+A: 36 -2456 2565 4 0 45
+A: 39 0 65535 0 0 0
+E: 0.000001 0004 0005 1234
+E: 0.000001 0000 0000 0000
+E: 1000000000.000000 0004 0005 1235
+E: 1000000000.000000 0000 0000 0000
diff --git a/core/api/current.txt b/core/api/current.txt
index ed8ab06..14e7493 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -145,12 +145,12 @@
field public static final String MANAGE_DEVICE_POLICY_AUDIO_OUTPUT = "android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT";
field public static final String MANAGE_DEVICE_POLICY_AUTOFILL = "android.permission.MANAGE_DEVICE_POLICY_AUTOFILL";
field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE";
- field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL = "android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL";
+ field public static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL = "android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL";
field public static final String MANAGE_DEVICE_POLICY_BLUETOOTH = "android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH";
field public static final String MANAGE_DEVICE_POLICY_BUGREPORT = "android.permission.MANAGE_DEVICE_POLICY_BUGREPORT";
field public static final String MANAGE_DEVICE_POLICY_CALLS = "android.permission.MANAGE_DEVICE_POLICY_CALLS";
field public static final String MANAGE_DEVICE_POLICY_CAMERA = "android.permission.MANAGE_DEVICE_POLICY_CAMERA";
- field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE";
+ field public static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE";
field public static final String MANAGE_DEVICE_POLICY_CERTIFICATES = "android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES";
field public static final String MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE = "android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE";
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String MANAGE_DEVICE_POLICY_CONTENT_PROTECTION = "android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION";
@@ -172,7 +172,7 @@
field public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS";
field public static final String MANAGE_DEVICE_POLICY_METERED_DATA = "android.permission.MANAGE_DEVICE_POLICY_METERED_DATA";
field public static final String MANAGE_DEVICE_POLICY_MICROPHONE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE";
- field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE";
+ field public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE";
field public static final String MANAGE_DEVICE_POLICY_MOBILE_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK";
field public static final String MANAGE_DEVICE_POLICY_MODIFY_USERS = "android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS";
field public static final String MANAGE_DEVICE_POLICY_MTE = "android.permission.MANAGE_DEVICE_POLICY_MTE";
@@ -7964,13 +7964,13 @@
field public static final String LOCK_TASK_POLICY = "lockTask";
field public static final String PACKAGES_SUSPENDED_POLICY = "packagesSuspended";
field public static final String PACKAGE_UNINSTALL_BLOCKED_POLICY = "packageUninstallBlocked";
- field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
+ field public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
field public static final String PERMISSION_GRANT_POLICY = "permissionGrant";
field public static final String PERSISTENT_PREFERRED_ACTIVITY_POLICY = "persistentPreferredActivity";
field public static final String RESET_PASSWORD_TOKEN_POLICY = "resetPasswordToken";
field public static final String SECURITY_LOGGING_POLICY = "securityLogging";
field public static final String STATUS_BAR_DISABLED_POLICY = "statusBarDisabled";
- field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
+ field public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
field public static final String USER_CONTROL_DISABLED_PACKAGES_POLICY = "userControlDisabledPackages";
}
@@ -8121,7 +8121,7 @@
method public boolean isLogoutEnabled();
method public boolean isManagedProfile(@NonNull android.content.ComponentName);
method public boolean isMasterVolumeMuted(@NonNull android.content.ComponentName);
- method @FlaggedApi("android.app.admin.flags.is_mte_policy_enforced") public static boolean isMtePolicyEnforced();
+ method public static boolean isMtePolicyEnforced();
method public boolean isNetworkLoggingEnabled(@Nullable android.content.ComponentName);
method public boolean isOrganizationOwnedDeviceWithManagedProfile();
method public boolean isOverrideApnEnabled(@NonNull android.content.ComponentName);
@@ -8597,7 +8597,7 @@
field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
- field @FlaggedApi("android.app.admin.flags.backup_service_security_log_event_enabled") public static final int TAG_BACKUP_SERVICE_TOGGLED = 210044; // 0x3347c
+ field public static final int TAG_BACKUP_SERVICE_TOGGLED = 210044; // 0x3347c
field public static final int TAG_BLUETOOTH_CONNECTION = 210039; // 0x33477
field public static final int TAG_BLUETOOTH_DISCONNECTION = 210040; // 0x33478
field public static final int TAG_CAMERA_POLICY_SET = 210034; // 0x33472
@@ -8756,6 +8756,8 @@
method public int getResultCode();
method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
method public boolean isSuccess();
+ method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
+ method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
field public static final String PROPERTY_RETURN_VALUE = "returnValue";
@@ -8767,13 +8769,6 @@
field public static final int RESULT_TIMED_OUT = 5; // 0x5
}
- @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final class ExecuteAppFunctionResponse.Builder {
- ctor public ExecuteAppFunctionResponse.Builder(@NonNull android.app.appsearch.GenericDocument);
- ctor public ExecuteAppFunctionResponse.Builder(int, @NonNull String);
- method @NonNull public android.app.appfunctions.ExecuteAppFunctionResponse build();
- method @NonNull public android.app.appfunctions.ExecuteAppFunctionResponse.Builder setExtras(@NonNull android.os.Bundle);
- }
-
}
package android.app.assist {
@@ -32776,6 +32771,7 @@
field @NonNull public static final String RELEASE_OR_PREVIEW_DISPLAY;
field @Deprecated public static final String SDK;
field public static final int SDK_INT;
+ field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int SDK_MINOR_INT;
field public static final String SECURITY_PATCH;
}
@@ -34270,9 +34266,14 @@
method public final int areAllEffectsSupported(@NonNull int...);
method public final boolean areAllPrimitivesSupported(@NonNull int...);
method @NonNull public int[] areEffectsSupported(@NonNull int...);
+ method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public boolean areEnvelopeEffectsSupported();
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
method public int getId();
+ method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectControlPointDurationMillis();
+ method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectDurationMillis();
+ method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectSize();
+ method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMinEnvelopeEffectControlPointDurationMillis();
method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
method public float getQFactor();
method public float getResonantFrequency();
@@ -43968,11 +43969,11 @@
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_esos_inactivity_timeout_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_p2p_sms_inactivity_timeout_sec_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_esos_inactivity_timeout_sec_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_p2p_sms_inactivity_timeout_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = "satellite_roaming_p2p_sms_supported_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_screen_off_inactivity_timeout_sec_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_screen_off_inactivity_timeout_sec_int";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -44071,6 +44072,7 @@
}
public static final class CarrierConfigManager.Gps {
+ field @FlaggedApi("android.location.flags.enable_ni_supl_message_injection_by_carrier_config") public static final String KEY_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL = "gps.enable_ni_supl_message_injection_bool";
field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool";
field public static final String KEY_PREFIX = "gps.";
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f26522b..fb425a9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -61,6 +61,7 @@
field @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final String BIND_DOMAIN_SELECTION_SERVICE = "android.permission.BIND_DOMAIN_SELECTION_SERVICE";
field public static final String BIND_DOMAIN_VERIFICATION_AGENT = "android.permission.BIND_DOMAIN_VERIFICATION_AGENT";
field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
+ field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String BIND_EXPLICIT_HEALTH_CHECK_SERVICE = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
field public static final String BIND_FIELD_CLASSIFICATION_SERVICE = "android.permission.BIND_FIELD_CLASSIFICATION_SERVICE";
field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE";
@@ -1321,7 +1322,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.app.admin.DevicePolicyState getDevicePolicyState();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public String getFinancedDeviceKioskRoleHolder();
- method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getMaxPolicyStorageLimit();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getMaxPolicyStorageLimit();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public java.util.List<android.os.UserHandle> getPolicyManagedProfiles(@NonNull android.os.UserHandle);
@@ -1348,7 +1349,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEventCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.admin.SecurityLog.SecurityEvent>>);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
- method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
@@ -3469,6 +3470,7 @@
public static interface VirtualDeviceManager.ActivityListener {
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onActivityLaunchBlocked(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.content.IntentSender);
method public void onDisplayEmpty(int);
+ method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onSecureWindowShown(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method @Deprecated public void onTopActivityChanged(int, @NonNull android.content.ComponentName);
method public default void onTopActivityChanged(int, @NonNull android.content.ComponentName, int);
}
@@ -4631,6 +4633,7 @@
method public int getCommittedSessionId();
method @NonNull public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
method public int getRollbackId();
+ method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public int getRollbackImpactLevel();
method public boolean isStaged();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
@@ -6746,6 +6749,14 @@
field public static final int STATUS_OK = 0; // 0x0
}
+ @FlaggedApi("android.media.soundtrigger.generic_model_api") public static final class SoundTrigger.GenericSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
+ ctor public SoundTrigger.GenericSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], int);
+ ctor public SoundTrigger.GenericSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[]);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.GenericSoundModel> CREATOR;
+ }
+
public static final class SoundTrigger.Keyphrase implements android.os.Parcelable {
ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]);
method public int describeContents();
@@ -10550,6 +10561,7 @@
method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean shouldDefaultToObserveMode();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
+ field @FlaggedApi("android.permission.flags.wallet_role_icon_property_enabled") public static final String PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL = "android.nfc.cardemulation.PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL";
}
@FlaggedApi("android.nfc.enable_nfc_mainline") public final class NfcFServiceInfo implements android.os.Parcelable {
@@ -18144,9 +18156,17 @@
field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0
}
+ @FlaggedApi("android.companion.virtualdevice.flags.status_bar_and_insets") public static class WindowManager.InsetsParams {
+ ctor public WindowManager.InsetsParams(int);
+ method @Nullable public android.graphics.Insets getInsetsSize();
+ method public int getType();
+ method @NonNull public android.view.WindowManager.InsetsParams setInsetsSize(@Nullable android.graphics.Insets);
+ }
+
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
method public final long getUserActivityTimeout();
method public boolean isSystemApplicationOverlay();
+ method @FlaggedApi("android.companion.virtualdevice.flags.status_bar_and_insets") public void setInsetsParams(@NonNull java.util.List<android.view.WindowManager.InsetsParams>);
method @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public void setSystemApplicationOverlay(boolean);
method public final void setUserActivityTimeout(long);
field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 009d082..4fc7076 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -597,19 +597,19 @@
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
- method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public void forceSetMaxPolicyStorageLimit(int);
+ method @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public void forceSetMaxPolicyStorageLimit(int);
method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
method @Deprecated public int getDeviceOwnerType(@NonNull android.content.ComponentName);
method @Nullable public String getDevicePolicyManagementRoleHolderUpdaterPackage();
method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
- method @FlaggedApi("android.app.admin.flags.headless_device_owner_provisioning_fix_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getHeadlessDeviceOwnerMode();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getHeadlessDeviceOwnerMode();
method public long getLastBugReportRequestTime();
method public long getLastNetworkLogRetrievalTime();
method public long getLastSecurityLogRetrievalTime();
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
- method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public int getPolicySizeForAdmin(@NonNull android.app.admin.EnforcingAdmin);
+ method @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public int getPolicySizeForAdmin(@NonNull android.app.admin.EnforcingAdmin);
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged();
@@ -680,7 +680,7 @@
}
public final class EnforcingAdmin implements android.os.Parcelable {
- ctor @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") public EnforcingAdmin(@NonNull String, @NonNull android.app.admin.Authority, @NonNull android.os.UserHandle, @Nullable android.content.ComponentName);
+ ctor public EnforcingAdmin(@NonNull String, @NonNull android.app.admin.Authority, @NonNull android.os.UserHandle, @Nullable android.content.ComponentName);
}
public final class FlagUnion extends android.app.admin.ResolutionMechanism<java.lang.Integer> {
@@ -1269,10 +1269,6 @@
package android.content.rollback {
- public final class RollbackInfo implements android.os.Parcelable {
- method @FlaggedApi("android.content.pm.recoverability_detection") public int getRollbackImpactLevel();
- }
-
public final class RollbackManager {
method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void blockRollbackManager(long);
method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
@@ -1801,11 +1797,15 @@
public class InputSettings {
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_bounce_keys_flag") public static int getAccessibilityBounceKeysThreshold(@NonNull android.content.Context);
+ method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") public static int getAccessibilityRepeatKeysDelay(@NonNull android.content.Context);
+ method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") public static int getAccessibilityRepeatKeysTimeout(@NonNull android.content.Context);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_slow_keys_flag") public static int getAccessibilitySlowKeysThreshold(@NonNull android.content.Context);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_mouse_keys") public static boolean isAccessibilityMouseKeysEnabled(@NonNull android.content.Context);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_sticky_keys_flag") public static boolean isAccessibilityStickyKeysEnabled(@NonNull android.content.Context);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_bounce_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityBounceKeysThreshold(@NonNull android.content.Context, int);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_mouse_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityMouseKeysEnabled(@NonNull android.content.Context, boolean);
+ method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityRepeatKeysDelay(@NonNull android.content.Context, int);
+ method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityRepeatKeysTimeout(@NonNull android.content.Context, int);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_slow_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilitySlowKeysThreshold(@NonNull android.content.Context, int);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_sticky_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityStickyKeysEnabled(@NonNull android.content.Context, boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, @FloatRange(from=0, to=1) float);
@@ -2003,6 +2003,7 @@
method public void setRampingRingerEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void setRs2Value(float);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void setVolumeControllerLongPressTimeoutEnabled(boolean);
method @FlaggedApi("android.media.audio.focus_exclusive_with_recording") @RequiresPermission(android.Manifest.permission.QUERY_AUDIO_STATE) public boolean shouldNotificationSoundPlay(@NonNull android.media.AudioAttributes);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 68063c4..7273e64 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -66,6 +66,7 @@
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IpcDataCache;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
@@ -87,6 +88,7 @@
import android.view.WindowInsetsController.Appearance;
import android.window.TaskSnapshot;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.RoSystemProperties;
@@ -237,6 +239,60 @@
private static final RateLimitingCache<List<ProcessErrorStateInfo>> mErrorProcessesCache =
new RateLimitingCache<>(10, 2);
+ /** Rate-Limiting cache that allows no more than 100 calls to the service per second. */
+ @GuardedBy("mMemoryInfoCache")
+ private static final RateLimitingCache<MemoryInfo> mMemoryInfoCache =
+ new RateLimitingCache<>(10);
+ /** Used to store cached results for rate-limited calls to getMemoryInfo(). */
+ @GuardedBy("mMemoryInfoCache")
+ private static final MemoryInfo mRateLimitedMemInfo = new MemoryInfo();
+
+ /** Rate-Limiting cache that allows no more than 200 calls to the service per second. */
+ @GuardedBy("mMyMemoryStateCache")
+ private static final RateLimitingCache<RunningAppProcessInfo> mMyMemoryStateCache =
+ new RateLimitingCache<>(10, 2);
+ /** Used to store cached results for rate-limited calls to getMyMemoryState(). */
+ @GuardedBy("mMyMemoryStateCache")
+ private static final RunningAppProcessInfo mRateLimitedMemState = new RunningAppProcessInfo();
+
+ /**
+ * Query handler for mGetCurrentUserIdCache - returns a cached value of the current foreground
+ * user id if the backstage_power/android.app.cache_get_current_user_id flag is enabled.
+ */
+ private static final IpcDataCache.QueryHandler<Void, Integer> mGetCurrentUserIdQuery =
+ new IpcDataCache.QueryHandler<>() {
+ @Override
+ public Integer apply(Void query) {
+ try {
+ return getService().getCurrentUserId();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public boolean shouldBypassCache(Void query) {
+ // If the flag to enable the new caching behavior is off, bypass the cache.
+ return !Flags.cacheGetCurrentUserId();
+ }
+ };
+
+ /** A cache which maintains the current foreground user id. */
+ private static final IpcDataCache<Void, Integer> mGetCurrentUserIdCache =
+ new IpcDataCache<>(1, IpcDataCache.MODULE_SYSTEM,
+ /* api= */ "getCurrentUserId", /* cacheName= */ "CurrentUserIdCache",
+ mGetCurrentUserIdQuery);
+
+ /**
+ * The current foreground user has changed - invalidate the cache. Currently only called from
+ * UserController when a user switch occurs.
+ * @hide
+ */
+ public static void invalidateGetCurrentUserIdCache() {
+ IpcDataCache.invalidateCache(
+ IpcDataCache.MODULE_SYSTEM, /* api= */ "getCurrentUserId");
+ }
+
/**
* Map of callbacks that have registered for {@link UidFrozenStateChanged} events.
* Will be called when a Uid has become frozen or unfrozen.
@@ -3471,6 +3527,19 @@
foregroundAppThreshold = source.readLong();
}
+ /** @hide */
+ public void copyTo(MemoryInfo other) {
+ other.advertisedMem = advertisedMem;
+ other.availMem = availMem;
+ other.totalMem = totalMem;
+ other.threshold = threshold;
+ other.lowMemory = lowMemory;
+ other.hiddenAppThreshold = hiddenAppThreshold;
+ other.secondaryServerThreshold = secondaryServerThreshold;
+ other.visibleAppThreshold = visibleAppThreshold;
+ other.foregroundAppThreshold = foregroundAppThreshold;
+ }
+
public static final @android.annotation.NonNull Creator<MemoryInfo> CREATOR
= new Creator<MemoryInfo>() {
public MemoryInfo createFromParcel(Parcel source) {
@@ -3497,6 +3566,20 @@
* manage its memory.
*/
public void getMemoryInfo(MemoryInfo outInfo) {
+ if (Flags.rateLimitGetMemoryInfo()) {
+ synchronized (mMemoryInfoCache) {
+ mMemoryInfoCache.get(() -> {
+ getMemoryInfoInternal(mRateLimitedMemInfo);
+ return mRateLimitedMemInfo;
+ });
+ mRateLimitedMemInfo.copyTo(outInfo);
+ }
+ } else {
+ getMemoryInfoInternal(outInfo);
+ }
+ }
+
+ private void getMemoryInfoInternal(MemoryInfo outInfo) {
try {
getService().getMemoryInfo(outInfo);
} catch (RemoteException e) {
@@ -4148,6 +4231,23 @@
lastActivityTime = source.readLong();
}
+ /**
+ * Note: only fields that are updated in ProcessList.fillInProcMemInfoLOSP() are copied.
+ * @hide
+ */
+ public void copyTo(RunningAppProcessInfo other) {
+ other.pid = pid;
+ other.uid = uid;
+ other.flags = flags;
+ other.lastTrimLevel = lastTrimLevel;
+ other.importance = importance;
+ other.lru = lru;
+ other.importanceReasonCode = importanceReasonCode;
+ other.processState = processState;
+ other.isFocused = isFocused;
+ other.lastActivityTime = lastActivityTime;
+ }
+
public static final @android.annotation.NonNull Creator<RunningAppProcessInfo> CREATOR =
new Creator<RunningAppProcessInfo>() {
public RunningAppProcessInfo createFromParcel(Parcel source) {
@@ -4779,7 +4879,21 @@
* {@link RunningAppProcessInfo#lru}, and
* {@link RunningAppProcessInfo#importanceReasonCode}.
*/
- static public void getMyMemoryState(RunningAppProcessInfo outState) {
+ public static void getMyMemoryState(RunningAppProcessInfo outState) {
+ if (Flags.rateLimitGetMyMemoryState()) {
+ synchronized (mMyMemoryStateCache) {
+ mMyMemoryStateCache.get(() -> {
+ getMyMemoryStateInternal(mRateLimitedMemState);
+ return mRateLimitedMemState;
+ });
+ mRateLimitedMemState.copyTo(outState);
+ }
+ } else {
+ getMyMemoryStateInternal(outState);
+ }
+ }
+
+ private static void getMyMemoryStateInternal(RunningAppProcessInfo outState) {
try {
getService().getMyMemoryState(outState);
} catch (RemoteException e) {
@@ -5244,11 +5358,7 @@
})
@android.ravenwood.annotation.RavenwoodReplace
public static int getCurrentUser() {
- try {
- return getService().getCurrentUserId();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mGetCurrentUserIdCache.query(null);
}
/** @hide */
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 21396a1..f27dc32 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -79,7 +79,6 @@
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Log;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.Pools;
@@ -3156,12 +3155,6 @@
/** @hide */
public static final String KEY_HISTORICAL_OPS = "historical_ops";
- /** System properties for debug logging of noteOp call sites */
- private static final String DEBUG_LOGGING_ENABLE_PROP = "appops.logging_enabled";
- private static final String DEBUG_LOGGING_PACKAGES_PROP = "appops.logging_packages";
- private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
- private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
-
/**
* Retrieve the op switch that controls the given operation.
* @hide
@@ -7459,15 +7452,15 @@
}
/**
- * Similar to {@link #onOpChanged(String, String, int)} but includes the device for which
- * the op mode has changed.
+ * Similar to {@link #onOpChanged(String, String)} but includes user and the device for
+ * which the op mode has changed.
*
* <p> Implement this method if callbacks are required on all devices.
* If not implemented explicitly, the default implementation will notify for op changes
- * on the default device {@link VirtualDeviceManager#PERSISTENT_DEVICE_ID_DEFAULT} only.
+ * on the default device only.
*
- * <p> If implemented, {@link #onOpChanged(String, String, int)}
- * will not be called automatically.
+ * <p> If implemented, {@link #onOpChanged(String, String)} will not be called
+ * automatically.
*
* @param op The Op that changed.
* @param packageName Package of the app whose Op changed.
@@ -8066,14 +8059,6 @@
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(int code, int uid, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Log.i(DEBUG_LOGGING_TAG,
- "setUidMode called for OP_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setUidMode(code, uid, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8094,15 +8079,6 @@
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (appOp.equals(OPSTR_BLUETOOTH_CONNECT)) {
- Log.i(DEBUG_LOGGING_TAG,
- "setUidMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
-
mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8143,14 +8119,6 @@
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setMode(int code, int uid, String packageName, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Log.i(DEBUG_LOGGING_TAG,
- "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setMode(code, uid, packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8173,14 +8141,6 @@
public void setMode(@NonNull String op, int uid, @Nullable String packageName,
@Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (op.equals(OPSTR_BLUETOOTH_CONNECT)) {
- Log.i(DEBUG_LOGGING_TAG,
- "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setMode(strOpToOp(op), uid, packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 62b5412..e0a9371 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -49,7 +49,7 @@
/**
* Rule is of an unknown type. This is the default value if not provided by the owning app,
- * and the value returned if the true type was added in an API level lower than the calling
+ * and the value returned if the true type was added in an API level higher than the calling
* app's targetSdk.
*/
@FlaggedApi(Flags.FLAG_MODES_API)
@@ -315,7 +315,8 @@
}
/**
- * Gets the zen policy.
+ * Gets the {@link ZenPolicy} applied if {@link #getInterruptionFilter()} is
+ * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}.
*/
@Nullable
public ZenPolicy getZenPolicy() {
@@ -345,6 +346,17 @@
/**
* Sets the interruption filter that is applied when this rule is active.
+ *
+ * <ul>
+ * <li>When {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}, the rule will use
+ * the {@link ZenPolicy} supplied to {@link #setZenPolicy} (or a default one).
+ * <li>When {@link NotificationManager#INTERRUPTION_FILTER_ALARMS} or
+ * {@link NotificationManager#INTERRUPTION_FILTER_NONE}, the rule will use a fixed
+ * {@link ZenPolicy} matching the filter.
+ * <li>When {@link NotificationManager#INTERRUPTION_FILTER_ALL}, the rule will not block
+ * notifications, but can still have {@link ZenDeviceEffects}.
+ * </ul>
+ *
* @param interruptionFilter The do not disturb mode to enter when this rule is active.
*/
public void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
@@ -374,7 +386,8 @@
}
/**
- * Sets the zen policy.
+ * Sets the {@link ZenPolicy} applied if {@link #getInterruptionFilter()} is
+ * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}.
*
* <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule},
* a {@code null} value here means the previous policy is retained.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e99ba84..7a36fbb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2706,14 +2706,9 @@
if (mAllowlistToken == null) {
mAllowlistToken = processAllowlistToken;
}
- if (Flags.secureAllowlistToken()) {
- // Propagate this token to all pending intents that are unmarshalled from the parcel,
- // or keep the one we're already propagating, if that's the case.
- if (!parcel.hasClassCookie(PendingIntent.class)) {
- parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
- }
- } else {
- // Propagate this token to all pending intents that are unmarshalled from the parcel.
+ // Propagate this token to all pending intents that are unmarshalled from the parcel,
+ // or keep the one we're already propagating, if that's the case.
+ if (!parcel.hasClassCookie(PendingIntent.class)) {
parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
}
@@ -3333,28 +3328,22 @@
PendingIntent.addOnMarshaledListener(addedListener);
}
try {
- if (Flags.secureAllowlistToken()) {
- boolean mustClearCookie = false;
- if (!parcel.hasClassCookie(Notification.class)) {
- // This is the "root" notification, and not an "inner" notification (including
- // publicVersion or anything else that might be embedded in extras). So we want
- // to use its token for every inner notification (might be null).
- parcel.setClassCookie(Notification.class, mAllowlistToken);
- mustClearCookie = true;
- }
- try {
- // IMPORTANT: Add marshaling code in writeToParcelImpl as we
- // want to intercept all pending events written to the parcel.
- writeToParcelImpl(parcel, flags);
- } finally {
- if (mustClearCookie) {
- parcel.removeClassCookie(Notification.class, mAllowlistToken);
- }
- }
- } else {
+ boolean mustClearCookie = false;
+ if (!parcel.hasClassCookie(Notification.class)) {
+ // This is the "root" notification, and not an "inner" notification (including
+ // publicVersion or anything else that might be embedded in extras). So we want
+ // to use its token for every inner notification (might be null).
+ parcel.setClassCookie(Notification.class, mAllowlistToken);
+ mustClearCookie = true;
+ }
+ try {
// IMPORTANT: Add marshaling code in writeToParcelImpl as we
// want to intercept all pending events written to the parcel.
writeToParcelImpl(parcel, flags);
+ } finally {
+ if (mustClearCookie) {
+ parcel.removeClassCookie(Notification.class, mAllowlistToken);
+ }
}
synchronized (this) {
@@ -3371,13 +3360,9 @@
private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
- if (Flags.secureAllowlistToken()) {
- // Always use the same token as the root notification (might be null).
- IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
- parcel.writeStrongBinder(rootNotificationToken);
- } else {
- parcel.writeStrongBinder(mAllowlistToken);
- }
+ // Always use the same token as the root notification (might be null).
+ IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
+ parcel.writeStrongBinder(rootNotificationToken);
parcel.writeLong(when);
parcel.writeLong(creationTime);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 789c99d..32e9542 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -168,7 +168,11 @@
/**
* @hide
*/
- public static final int MAX_VIBRATION_LENGTH = 1000;
+ public static final int MAX_VIBRATION_LENGTH = 500;
+ /**
+ * @hide
+ */
+ public static final int MAX_SERIALIZED_VIBRATION_LENGTH = 32_768;
private static final String TAG_CHANNEL = "channel";
private static final String ATT_NAME = "name";
@@ -368,6 +372,9 @@
if (Flags.notificationChannelVibrationEffectApi()) {
mVibrationEffect =
in.readInt() != 0 ? VibrationEffect.CREATOR.createFromParcel(in) : null;
+ if (Flags.notifChannelCropVibrationEffects() && mVibrationEffect != null) {
+ mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect);
+ }
}
mUserLockedFields = in.readInt();
mUserVisibleTaskShown = in.readByte() != 0;
@@ -582,6 +589,23 @@
return input;
}
+ // Returns trimmed vibration effect or null if not trimmable.
+ private VibrationEffect getTrimmedVibrationEffect(VibrationEffect effect) {
+ if (effect == null) {
+ return null;
+ }
+ // trim if possible; check serialized length; reject if it is still too long
+ VibrationEffect result = effect;
+ VibrationEffect trimmed = effect.cropToLengthOrNull(MAX_VIBRATION_LENGTH);
+ if (trimmed != null) {
+ result = trimmed;
+ }
+ if (vibrationToString(result).length() > MAX_SERIALIZED_VIBRATION_LENGTH) {
+ return null;
+ }
+ return result;
+ }
+
/**
* @hide
*/
@@ -685,6 +709,11 @@
public void setVibrationPattern(long[] vibrationPattern) {
this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
this.mVibrationPattern = vibrationPattern;
+ if (Flags.notifChannelCropVibrationEffects()) {
+ if (vibrationPattern != null && vibrationPattern.length > MAX_VIBRATION_LENGTH) {
+ this.mVibrationPattern = Arrays.copyOf(vibrationPattern, MAX_VIBRATION_LENGTH);
+ }
+ }
if (Flags.notificationChannelVibrationEffectApi()) {
try {
this.mVibrationEffect =
@@ -731,9 +760,29 @@
public void setVibrationEffect(@Nullable VibrationEffect effect) {
this.mVibrationEnabled = effect != null;
this.mVibrationEffect = effect;
- this.mVibrationPattern =
- effect == null
- ? null : effect.computeCreateWaveformOffOnTimingsOrNull();
+ if (Flags.notifChannelCropVibrationEffects() && effect != null) {
+ long[] pattern = effect.computeCreateWaveformOffOnTimingsOrNull();
+ if (pattern != null) {
+ // If this effect has an equivalent pattern, AND the pattern needs to be truncated
+ // due to being too long, we delegate to setVibrationPattern to re-generate the
+ // effect as well. Otherwise, we use the effect (already set above) and converted
+ // pattern directly.
+ if (pattern.length > MAX_VIBRATION_LENGTH) {
+ setVibrationPattern(pattern);
+ } else {
+ this.mVibrationPattern = pattern;
+ }
+ } else {
+ // If not convertible to a pattern directly, try trimming the vibration effect if
+ // possible and storing that version instead.
+ this.mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect);
+ this.mVibrationPattern = null;
+ }
+ } else {
+ this.mVibrationPattern =
+ mVibrationEffect == null
+ ? null : mVibrationEffect.computeCreateWaveformOffOnTimingsOrNull();
+ }
}
/**
@@ -1172,7 +1221,9 @@
if (vibrationEffect != null) {
// Restore the effect only if it is not null. This allows to avoid undoing a
// `setVibrationPattern` call above, if that was done with a non-null pattern
- // (e.g. back up from a version that did not support `setVibrationEffect`).
+ // (e.g. back up from a version that did not support `setVibrationEffect`), or
+ // when notif_channel_crop_vibration_effects is true, if there is an equivalent
+ // vibration pattern available.
setVibrationEffect(vibrationEffect);
}
}
@@ -1365,7 +1416,11 @@
out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
}
if (getVibrationEffect() != null) {
- out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+ if (!Flags.notifChannelCropVibrationEffects() || getVibrationPattern() == null) {
+ // When notif_channel_crop_vibration_effects is on, only serialize the vibration
+ // effect if we do not already have an equivalent vibration pattern.
+ out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+ }
}
if (getUserLockedFields() != 0) {
out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 8b3ee24..e44e776 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -31,6 +31,7 @@
import android.app.ambientcontext.AmbientContextManager;
import android.app.ambientcontext.IAmbientContextManager;
import android.app.appfunctions.AppFunctionManager;
+import android.app.appfunctions.AppFunctionManagerConfiguration;
import android.app.appfunctions.IAppFunctionManager;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
@@ -937,8 +938,10 @@
@Override
public AppFunctionManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
+ if (!AppFunctionManagerConfiguration.isSupported(ctx)) {
+ return null;
+ }
IAppFunctionManager service;
- //TODO(b/357551503): If the feature not present avoid look up every time
service = IAppFunctionManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.APP_FUNCTION_SERVICE));
return new AppFunctionManager(service, ctx.getOuterContext());
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 32e6e80..38bd576 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -104,3 +104,46 @@
}
}
+flag {
+ namespace: "backstage_power"
+ name: "use_app_info_not_launched"
+ description: "Use the notLaunched state from ApplicationInfo instead of current value"
+ is_fixed_read_only: true
+ bug: "362516211"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "backstage_power"
+ name: "cache_get_current_user_id"
+ description: "Add caching for getCurrentUserId"
+ is_fixed_read_only: true
+ bug: "361853873"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "backstage_power"
+ name: "rate_limit_get_memory_info"
+ description: "Rate limit calls to getMemoryInfo using a cache"
+ is_fixed_read_only: true
+ bug: "364312431"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "backstage_power"
+ name: "rate_limit_get_my_memory_state"
+ description: "Rate limit calls to getMyMemoryState using a cache"
+ is_fixed_read_only: true
+ bug: "365182205"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index 02e492b..515c1c6 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -24,7 +24,6 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -54,9 +53,7 @@
@TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
- }
+ PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
mAccountType = Objects.requireNonNull((accountType));
}
diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java
index c993671..00e67e6 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -31,9 +30,7 @@
public BundlePolicyValue(Bundle value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
- }
+ PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
}
private BundlePolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java
index a7a2f7d..f092b7b 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.Parcel;
@@ -31,9 +30,7 @@
public ComponentNamePolicyValue(@NonNull ComponentName value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxComponentNameLength(value);
- }
+ PolicySizeVerifier.enforceMaxComponentNameLength(value);
}
private ComponentNamePolicyValue(Parcel source) {
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/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 156512a..c0e435c 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -16,8 +16,6 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED;
-
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -185,13 +183,11 @@
/**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
*/
- @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
/**
* String identifier for {@link DevicePolicyManager#setRequiredPasswordComplexity}.
*/
- @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d31d8f2..daa15f0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -54,13 +54,9 @@
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
-import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -4234,7 +4230,6 @@
*
* @return whether MTE is currently enabled on the device.
*/
- @FlaggedApi(FLAG_IS_MTE_POLICY_ENFORCED)
public static boolean isMtePolicyEnforced() {
return Zygote.nativeSupportsMemoryTagging();
}
@@ -8666,6 +8661,7 @@
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_CAMERA, conditional = true)
+ @SupportsCoexistence
public void setCameraDisabled(@Nullable ComponentName admin, boolean disabled) {
if (mService != null) {
try {
@@ -10251,6 +10247,7 @@
* permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_TASK}.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK_TASK, conditional = true)
+ @SupportsCoexistence
public void clearPackagePersistentPreferredActivities(@Nullable ComponentName admin,
String packageName) {
throwIfParentInstance("clearPackagePersistentPreferredActivities");
@@ -10478,10 +10475,6 @@
@WorkerThread
public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
Bundle settings) {
- if (!Flags.dmrhSetAppRestrictions()) {
- throwIfParentInstance("setApplicationRestrictions");
- }
-
if (mService != null) {
try {
mService.setApplicationRestrictions(admin, mContext.getPackageName(), packageName,
@@ -11886,9 +11879,6 @@
@WorkerThread
public @NonNull Bundle getApplicationRestrictions(
@Nullable ComponentName admin, String packageName) {
- if (!Flags.dmrhSetAppRestrictions()) {
- throwIfParentInstance("getApplicationRestrictions");
- }
if (mService != null) {
try {
@@ -11949,6 +11939,7 @@
* @throws SecurityException if {@code admin} is not a device or profile owner and if the caller
* has not been granted the permission to set the given user restriction.
*/
+ @SupportsCoexistence
public void addUserRestriction(@NonNull ComponentName admin,
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
@@ -12030,6 +12021,7 @@
* @throws IllegalStateException if caller is not targeting Android
* {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above.
*/
+ @SupportsCoexistence
public void addUserRestrictionGlobally(@NonNull @UserManager.UserRestrictionKey String key) {
throwIfParentInstance("addUserRestrictionGlobally");
if (mService != null) {
@@ -12085,6 +12077,7 @@
* @throws SecurityException if {@code admin} is not a device or profile owner and if the
* caller has not been granted the permission to set the given user restriction.
*/
+ @SupportsCoexistence
public void clearUserRestriction(@NonNull ComponentName admin,
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
@@ -12321,6 +12314,7 @@
* @see #DELEGATION_PACKAGE_ACCESS
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_PACKAGE_STATE, conditional = true)
+ @SupportsCoexistence
public boolean setApplicationHidden(@Nullable ComponentName admin, String packageName,
boolean hidden) {
if (mService != null) {
@@ -12501,6 +12495,7 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, conditional = true)
+ @SupportsCoexistence
public void setAccountManagementDisabled(@Nullable ComponentName admin, String accountType,
boolean disabled) {
if (mService != null) {
@@ -12584,10 +12579,24 @@
**/
@SystemApi
public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) {
+ setSecondaryLockscreenEnabled(admin, enabled, null);
+ }
+
+ /**
+ * Called by the system supervision app to set whether a secondary lockscreen needs to be shown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
+ * caller is not a device admin.
+ * @param enabled Whether or not the lockscreen needs to be shown.
+ * @param options A {@link PersistableBundle} to supply options to the lock screen.
+ * @hide
+ */
+ public void setSecondaryLockscreenEnabled(@Nullable ComponentName admin, boolean enabled,
+ @Nullable PersistableBundle options) {
throwIfParentInstance("setSecondaryLockscreenEnabled");
if (mService != null) {
try {
- mService.setSecondaryLockscreenEnabled(admin, enabled);
+ mService.setSecondaryLockscreenEnabled(admin, enabled, options);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -14233,21 +14242,11 @@
*/
public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
throwIfParentInstance("getParentProfileInstance");
- try {
- if (Flags.dmrhSetAppRestrictions()) {
- UserManager um = mContext.getSystemService(UserManager.class);
- if (!um.isManagedProfile()) {
- throw new SecurityException("The current user does not have a parent profile.");
- }
- } else {
- if (!mService.isManagedProfile(admin)) {
- throw new SecurityException("The current user does not have a parent profile.");
- }
- }
- return new DevicePolicyManager(mContext, mService, true);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ UserManager um = mContext.getSystemService(UserManager.class);
+ if (!um.isManagedProfile()) {
+ throw new SecurityException("The current user does not have a parent profile.");
}
+ return new DevicePolicyManager(mContext, mService, true);
}
/**
@@ -14295,6 +14294,7 @@
* @see #retrieveSecurityLogs
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_SECURITY_LOGGING, conditional = true)
+ @SupportsCoexistence
public void setSecurityLoggingEnabled(@Nullable ComponentName admin, boolean enabled) {
throwIfParentInstance("setSecurityLoggingEnabled");
try {
@@ -17200,6 +17200,7 @@
* if USB data signaling fails to be enabled/disabled.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, conditional = true)
+ @SupportsCoexistence
public void setUsbDataSignalingEnabled(boolean enabled) {
throwIfParentInstance("setUsbDataSignalingEnabled");
if (mService != null) {
@@ -17765,7 +17766,6 @@
*/
@SystemApi
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED)
public void setMaxPolicyStorageLimit(int storageLimit) {
if (mService != null) {
try {
@@ -17785,7 +17785,6 @@
*/
@SystemApi
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED)
public int getMaxPolicyStorageLimit() {
if (mService != null) {
try {
@@ -17809,7 +17808,6 @@
*/
@TestApi
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
public void forceSetMaxPolicyStorageLimit(int storageLimit) {
if (mService != null) {
try {
@@ -17827,7 +17825,6 @@
*/
@TestApi
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
public int getPolicySizeForAdmin(@NonNull EnforcingAdmin admin) {
if (mService != null) {
try {
@@ -17846,13 +17843,9 @@
* @hide
*/
@TestApi
- @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED)
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
@DeviceAdminInfo.HeadlessDeviceOwnerMode
public int getHeadlessDeviceOwnerMode() {
- if (!Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
- return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
- }
if (mService != null) {
try {
return mService.getHeadlessDeviceOwnerMode(mContext.getPackageName());
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index f70a53f..5f9bb9c 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -16,9 +16,6 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
-
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -64,7 +61,6 @@
*
* @hide
*/
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
@TestApi
public EnforcingAdmin(
@NonNull String packageName, @NonNull Authority authority,
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d4e5c99..a4e2b8f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -303,7 +303,7 @@
String[] getAccountTypesWithManagementDisabled(String callerPackageName);
String[] getAccountTypesWithManagementDisabledAsUser(int userId, String callerPackageName, in boolean parent);
- void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled);
+ void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled, in PersistableBundle options);
boolean isSecondaryLockscreenEnabled(in UserHandle userHandle);
void setPreferentialNetworkServiceConfigs(
diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java
index 68b4ad8..ab32d46 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.app.admin.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -135,10 +134,8 @@
}
private void setPackagesInternal(Set<String> packages) {
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- for (String p : packages) {
- PolicySizeVerifier.enforceMaxPackageNameLength(p);
- }
+ for (String p : packages) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(p);
}
mPackages = new HashSet<>(packages);
}
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 1a04f6c..226c576 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -25,7 +25,6 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -59,10 +58,8 @@
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
- }
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
+ PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
mPackageName = Objects.requireNonNull((packageName));
mPermissionName = Objects.requireNonNull((permissionName));
}
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 9e31a23..8fa21db 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -24,7 +24,6 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -55,9 +54,7 @@
@TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- }
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
mPackageName = Objects.requireNonNull((packageName));
}
diff --git a/core/java/android/app/admin/PackageSetPolicyValue.java b/core/java/android/app/admin/PackageSetPolicyValue.java
index 8b253a2..24c50b0 100644
--- a/core/java/android/app/admin/PackageSetPolicyValue.java
+++ b/core/java/android/app/admin/PackageSetPolicyValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.HashSet;
@@ -32,10 +31,8 @@
public PackageSetPolicyValue(@NonNull Set<String> value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- for (String packageName : value) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- }
+ for (String packageName : value) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
}
}
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 477f2e0..beb93fd 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -16,10 +16,7 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED;
-
import android.Manifest;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -611,7 +608,6 @@
* <li> [2] backup service state ({@code Integer}, 1 for enabled, 0 for disabled)
* @see DevicePolicyManager#setBackupServiceEnabled(ComponentName, boolean)
*/
- @FlaggedApi(FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED)
public static final int TAG_BACKUP_SERVICE_TOGGLED =
SecurityLogTags.SECURITY_BACKUP_SERVICE_TOGGLED;
/**
diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java
index 6efe9ad..bb07c23 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.Objects;
@@ -30,9 +29,7 @@
public StringPolicyValue(@NonNull String value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
- }
+ PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
}
private StringPolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index 9054287..16cfba4 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -45,9 +44,7 @@
@TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
- }
+ PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
mRestriction = Objects.requireNonNull(restriction);
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 29a5048..081dfe6 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -4,6 +4,7 @@
package: "android.app.admin.flags"
container: "system"
+# Fully rolled out and must not be used.
flag {
name: "policy_engine_migration_v2_enabled"
is_exported: true
@@ -12,6 +13,7 @@
bug: "289520697"
}
+# Fully rolled out and must not be used.
flag {
name: "device_policy_size_tracking_enabled"
is_exported: true
@@ -21,23 +23,6 @@
}
flag {
- name: "device_policy_size_tracking_internal_enabled"
- namespace: "enterprise"
- description: "Add feature to track the total policy size and have a max threshold - internal changes"
- bug: "281543351"
-}
-
-flag {
- name: "device_policy_size_tracking_internal_bug_fix_enabled"
- namespace: "enterprise"
- description: "Bug fix for tracking the total policy size and have a max threshold"
- bug: "281543351"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "onboarding_bugreport_v2_enabled"
is_exported: true
namespace: "enterprise"
@@ -53,13 +38,7 @@
is_fixed_read_only: true
}
-flag {
- name: "dedicated_device_control_enabled"
- namespace: "enterprise"
- description: "Allow the device management role holder to control which platform features are available on dedicated devices."
- bug: "281964214"
-}
-
+# Fully rolled out and must not be used.
flag {
name: "dedicated_device_control_api_enabled"
is_exported: true
@@ -77,13 +56,6 @@
}
flag {
- name: "permission_migration_for_zero_trust_impl_enabled"
- namespace: "enterprise"
- description: "(Implementation) Migrate existing APIs to permission based, and enable DMRH to call them to collect Zero Trust signals."
- bug: "289520697"
-}
-
-flag {
name: "device_theft_api_enabled"
is_exported: true
namespace: "enterprise"
@@ -99,10 +71,67 @@
}
flag {
- name: "coexistence_migration_for_non_emm_management_enabled"
+ name: "coexistence_migration_for_supervision_enabled"
+ is_exported: true
namespace: "enterprise"
- description: "Migrate existing APIs to be coexistable, and enable DMRH to call them to support non-EMM device management."
- bug: "289520697"
+ description: "Migrate existing APIs that are used by supervision (Kids Module) to be coexistable."
+ bug: "356894721"
+}
+
+flag {
+ name: "reset_password_with_token_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for resetPasswordWithToken and setResetPasswordToken."
+ bug: "359187209"
+}
+
+flag {
+ name: "set_keyguard_disabled_features_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setKeyguardDisabledFeatures."
+ bug: "359186276"
+}
+
+flag {
+ name: "set_application_restrictions_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setApplicationRestrictions."
+ bug: "359188153"
+}
+
+flag {
+ name: "set_auto_time_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setAutoTimeEnabled."
+ bug: "359188869"
+}
+
+flag {
+ name: "set_backup_service_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setBackupServiceEnabled."
+ bug: "359188483"
+}
+
+flag {
+ name: "set_auto_time_zone_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setAutoTimeZoneEnabled."
+ bug: "364338300"
+}
+
+flag {
+ name: "set_permission_grant_state_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setPermissionGrantState."
+ bug: "364338410"
}
# Fully rolled out and must not be used.
@@ -115,16 +144,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"
@@ -133,6 +152,16 @@
}
flag {
+ name: "fix_race_condition_in_tie_profile_lock"
+ namespace: "enterprise"
+ description: "Fix race condition in tieProfileLockIfNecessary()"
+ bug: "355905501"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "quiet_mode_credential_bug_fix"
namespace: "enterprise"
description: "Guards a bugfix that ends the credential input flow if the managed user has not stopped."
@@ -158,6 +187,7 @@
}
}
+# Fully rolled out and must not be used.
flag {
name: "backup_service_security_log_event_enabled"
is_exported: true
@@ -184,6 +214,7 @@
bug: "289515470"
}
+# Fully rolled out and must not be used.
flag {
name: "is_mte_policy_enforced"
is_exported: true
@@ -193,16 +224,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"
@@ -220,43 +241,6 @@
}
flag {
- name: "headless_device_owner_provisioning_fix_enabled"
- namespace: "enterprise"
- description: "Fix provisioning for single-user headless DO"
- bug: "289515470"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "dmrh_set_app_restrictions"
- namespace: "enterprise"
- description: "Allow DMRH to set application restrictions (both on the profile and the parent)"
- bug: "328758346"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "allow_screen_brightness_control_on_cope"
- namespace: "enterprise"
- description: "Allow COPE admin to control screen brightness and timeout."
- bug: "323894620"
-}
-
-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"
@@ -264,26 +248,6 @@
}
flag {
- name: "headless_device_owner_delegate_security_logging_bug_fix"
- namespace: "enterprise"
- description: "Fix delegate security logging for single user headless DO."
- bug: "289515470"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-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"
@@ -294,16 +258,6 @@
}
flag {
- name: "delete_private_space_under_restriction"
- namespace: "enterprise"
- description: "Delete private space if user restriction is set"
- bug: "328758346"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "unmanaged_mode_migration"
namespace: "enterprise"
description: "Migrate APIs for unmanaged mode"
@@ -314,16 +268,6 @@
}
flag {
- name: "headless_single_user_fixes"
- namespace: "enterprise"
- description: "Various fixes for headless single user mode"
- bug: "289515470"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "backup_connected_apps_settings"
namespace: "enterprise"
description: "backup and restore connected work and personal apps user settings across devices"
@@ -331,16 +275,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 b6240a7..4682f3d 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -49,9 +49,8 @@
/**
* Creates an instance.
*
- * @param mService An interface to the backing service.
+ * @param service An interface to the backing service.
* @param context A {@link Context}.
- *
* @hide
*/
public AppFunctionManager(IAppFunctionManager service, Context context) {
@@ -61,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,
@@ -108,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(new ExecuteAppFunctionResponse.Builder(
- getResultCode(e), e.getMessage()).build());
+ 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
new file mode 100644
index 0000000..fa77e79
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+/**
+ * Represents the system configuration of support for the {@code AppFunctionManager} and associated
+ * systems.
+ *
+ * @hide
+ */
+public class AppFunctionManagerConfiguration {
+ private final Context mContext;
+
+ /**
+ * Constructs a new instance of {@code AppFunctionManagerConfiguration}.
+ *
+ * @param context context
+ */
+ public AppFunctionManagerConfiguration(@NonNull final Context context) {
+ mContext = context;
+ }
+
+ /**
+ * 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}
+ */
+ public static boolean isSupported(@NonNull final Context context) {
+ return new AppFunctionManagerConfiguration(context).isSupported();
+ }
+
+ private boolean isWatch() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+}
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..f5c5a11 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -56,16 +56,26 @@
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 package name from the runtime metadata schema name. */
+ @NonNull
+ public static String getPackageNameFromSchema(String metadataSchemaType) {
+ String[] split = metadataSchemaType.split(RUNTIME_SCHEMA_TYPE_SEPARATOR);
+ if (split.length > 2) {
+ throw new IllegalArgumentException(
+ "Invalid schema type: " + metadataSchemaType + " for app function runtime");
+ }
+ if (split.length < 2) {
+ return APP_FUNCTION_INDEXER_PACKAGE;
+ }
+ return split[1];
+ }
+
+ /** 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,15 +84,34 @@
/**
* 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.
+ * with {@link android.Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
+ * android.Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permissions.
*
* @param packageName The package name to create a schema for.
*/
@NonNull
public static AppSearchSchema createAppFunctionRuntimeSchema(@NonNull String packageName) {
- return new AppSearchSchema.Builder(getRuntimeSchemaNameForPackage(packageName))
+ return getAppFunctionRuntimeSchemaBuilder(getRuntimeSchemaNameForPackage(packageName))
+ .addParentType(RUNTIME_SCHEMA_TYPE)
+ .build();
+ }
+
+ /**
+ * Creates a parent schema for all app function runtime schemas.
+ *
+ * <p>This schema should be set visible to the owner itself and for callers with {@link
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link
+ * android.permission.EXECUTE_APP_FUNCTIONS} permissions.
+ */
+ public static AppSearchSchema createParentAppFunctionRuntimeSchema() {
+ return getAppFunctionRuntimeSchemaBuilder(RUNTIME_SCHEMA_TYPE).build();
+ }
+
+ private static AppSearchSchema.Builder getAppFunctionRuntimeSchemaBuilder(
+ @NonNull String schemaType) {
+ return new AppSearchSchema.Builder(schemaType)
.addProperty(
new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_FUNCTION_ID)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
@@ -109,27 +138,21 @@
.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
.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
- .build())
- .addParentType(RUNTIME_SCHEMA_TYPE)
- .build();
+ .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 +167,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 +178,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 +198,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 +212,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 6259d16..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,22 +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(
- new ExecuteAppFunctionResponse.Builder(
- getResultCode(ex), getExceptionMessage(ex)).build());
+ ExecuteAppFunctionResponse.newFailure(
+ getResultCode(ex), ex.getMessage(), /* extras= */ null));
}
}
};
@@ -110,15 +107,11 @@
* 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
public abstract void onExecuteFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull Consumer<ExecuteAppFunctionResponse> callback);
-
- private String getExceptionMessage(Exception exception) {
- return exception.getMessage() == null ? "" : exception.getMessage();
- }
}
diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
index 6d4172a..085e0a4 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
*/
@@ -39,15 +39,15 @@
public static final String STATIC_PROPERTY_ENABLED_BY_DEFAULT = "enabledByDefault";
public static final String APP_FUNCTION_STATIC_NAMESPACE = "app_functions";
+ public static final String PROPERTY_FUNCTION_ID = "functionId";
+ public static final String PROPERTY_PACKAGE_NAME = "packageName";
// These are constants that has to be kept the same with {@code
// com.android.server.appsearch.appsindexer.appsearchtypes.AppSearchHelper}.
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 +59,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 9fb3375..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;
@@ -162,14 +146,60 @@
}
/**
+ * 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.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ 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);
+ }
+
+ /**
+ * 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 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) {
+ 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);
+ }
+
+ private static Bundle getActualExtras(@Nullable Bundle extras) {
+ if (extras == null) {
+ return Bundle.EMPTY;
+ }
+ return extras;
+ }
+
+ /**
* 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);
@@ -185,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;
@@ -240,74 +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 {
- }
-
- /**
- * The builder for creating {@link ExecuteAppFunctionResponse} instances.
- */
- @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
- public static final class Builder {
- @NonNull
- private GenericDocument mResultDocument =
- new GenericDocument.Builder<>("", "", "").build();
- @NonNull
- private Bundle mExtras = Bundle.EMPTY;
- private int mResultCode;
- @Nullable
- private String mErrorMessage;
-
- /**
- * Creates a new builder for {@link ExecuteAppFunctionResponse}.
- */
- private Builder() {
- }
-
- /**
- * Creates a new builder for {@link ExecuteAppFunctionResponse} to build a success response
- * with a result code of {@link #RESULT_OK} and a resultDocument.
- */
- public Builder(@NonNull GenericDocument resultDocument) {
- Objects.requireNonNull(resultDocument);
- mResultDocument = resultDocument;
- mResultCode = RESULT_OK;
- }
-
- /**
- * Creates a new builder for {@link ExecuteAppFunctionResponse} to build an error response
- * with a result code and an error message.
- */
- public Builder(@ResultCode int resultCode,
- @NonNull String errorMessage) {
- mResultCode = resultCode;
- mErrorMessage = Objects.requireNonNull(errorMessage);
- }
-
- /**
- * Sets the extras of the app function execution.
- */
- @NonNull
- public Builder setExtras(@NonNull Bundle extras) {
- mExtras = Objects.requireNonNull(extras);
- return this;
- }
-
- /**
- * Builds the {@link ExecuteAppFunctionResponse} instance.
- */
- @NonNull
- public ExecuteAppFunctionResponse build() {
- return new ExecuteAppFunctionResponse(
- new GenericDocumentWrapper(mResultDocument),
- mExtras, mResultCode, mErrorMessage);
- }
- }
+ 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/app/jank/OWNERS b/core/java/android/app/jank/OWNERS
new file mode 100644
index 0000000..806de57
--- /dev/null
+++ b/core/java/android/app/jank/OWNERS
@@ -0,0 +1,4 @@
[email protected]
[email protected]
[email protected]
[email protected]
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 606ca33..9891e89 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -128,6 +128,16 @@
}
flag {
+ name: "notif_channel_crop_vibration_effects"
+ namespace: "systemui"
+ description: "Limits the size of vibration effects that can be stored in a NotificationChannel"
+ bug: "345881518"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "evenly_divided_call_style_action_layout"
namespace: "systemui"
description: "Evenly divides horizontal space for action buttons in CallStyle notifications."
diff --git a/core/java/android/app/supervision/OWNERS b/core/java/android/app/supervision/OWNERS
new file mode 100644
index 0000000..afc5495
--- /dev/null
+++ b/core/java/android/app/supervision/OWNERS
@@ -0,0 +1,2 @@
[email protected]
[email protected]
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 4e0379e..7117f25 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -57,3 +57,10 @@
description: "Enable support for persisting RemoteViews previews to Protobuf"
bug: "306546610"
}
+
+flag {
+ name: "remote_document_support"
+ namespace: "app_widgets"
+ description: "Remote document support features in Q2 2025 release"
+ bug: "339721781"
+}
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
index 7c674f9..767f52a 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
@@ -54,4 +54,13 @@
*/
void onActivityLaunchBlocked(int displayId, in ComponentName componentName, in UserHandle user,
in IntentSender intentSender);
+
+ /**
+ * Called when a secure surface is shown on the device.
+ *
+ * @param displayId The display ID on which the secure surface was shown.
+ * @param componentName The component name of the activity that showed the secure surface.
+ * @param user The user associated with the activity.
+ */
+ void onSecureWindowShown(int displayId, in ComponentName componentName, in UserHandle user);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index b7bf2d1..de20a68 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -151,7 +151,24 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void onSecureWindowShown(int displayId, ComponentName componentName,
+ UserHandle user) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mActivityListenersLock) {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i)
+ .onSecureWindowShown(displayId, componentName, user);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
+
private final IVirtualDeviceSoundEffectListener mSoundEffectListener =
new IVirtualDeviceSoundEffectListener.Stub() {
@Override
@@ -584,6 +601,12 @@
mActivityListener.onActivityLaunchBlocked(
displayId, componentName, user, intentSender));
}
+
+ public void onSecureWindowShown(int displayId, ComponentName componentName,
+ UserHandle user) {
+ mExecutor.execute(() ->
+ mActivityListener.onSecureWindowShown(displayId, componentName, user));
+ }
}
/**
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 40aa6837..473ab27 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -1255,6 +1255,22 @@
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
default void onActivityLaunchBlocked(int displayId, @NonNull ComponentName componentName,
@NonNull UserHandle user, @Nullable IntentSender intentSender) {}
+
+ /**
+ * 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.
+ *
+ * @see Display#FLAG_SECURE
+ * @see WindowManager.LayoutParams#FLAG_SECURE
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+ default void onSecureWindowShown(int displayId, @NonNull ComponentName componentName,
+ @NonNull UserHandle user) {}
}
/**
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 22a9ccf..e9fa3e1 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -43,6 +43,7 @@
name: "virtual_display_insets"
description: "APIs for specifying virtual display insets (via cutout)"
bug: "350007135"
+ is_exported: true
}
flag {
@@ -88,6 +89,7 @@
name: "virtual_display_rotation_api"
description: "API for on-demand rotation of virtual displays"
bug: "291748430"
+ is_exported: true
}
flag {
@@ -98,11 +100,11 @@
}
flag {
- name: "camera_multiple_input_streams"
- is_exported: true
- namespace: "virtual_devices"
- description: "Expose multiple surface for the virtual camera owner for different stream resolution"
- bug: "341083465"
+ name: "camera_multiple_input_streams"
+ is_exported: true
+ namespace: "virtual_devices"
+ description: "Expose multiple surface for the virtual camera owner for different stream resolution"
+ bug: "341083465"
}
flag {
@@ -110,4 +112,28 @@
name: "device_aware_display_power"
description: "Device awareness in power and display APIs"
bug: "285020111"
+ is_exported: true
+}
+
+flag {
+ namespace: "virtual_devices"
+ name: "display_power_manager_apis"
+ description: "Make relevant PowerManager APIs display aware by default"
+ bug: "365042486"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "status_bar_and_insets"
+ namespace: "virtual_devices"
+ description: "Allow for status bar and insets on virtual devices"
+ bug: "350007866"
+ is_exported: true
+}
+
+flag {
+ namespace: "virtual_devices"
+ name: "camera_timestamp_from_surface"
+ description: "Pass the surface timestamp to the capture result"
+ bug: "351341245"
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3bf0f032..12c5d07 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -33,6 +33,7 @@
import android.annotation.Nullable;
import android.annotation.PermissionMethod;
import android.annotation.PermissionName;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.StringDef;
import android.annotation.StringRes;
@@ -6678,6 +6679,8 @@
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.webkit.WebViewUpdateManager} for accessing the WebView update service.
*
+ * <p>This can only be used on devices with {@link PackageManager#FEATURE_WEBVIEW}.
+ *
* @see #getSystemService(String)
* @see android.webkit.WebViewUpdateManager
* @hide
@@ -6685,6 +6688,7 @@
@FlaggedApi(android.webkit.Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SuppressLint("ServiceName")
+ @RequiresFeature(PackageManager.FEATURE_WEBVIEW)
public static final String WEBVIEW_UPDATE_SERVICE = "webviewupdate";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index abb0d8d..031380d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -86,6 +86,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.XmlUtils;
+import com.android.modules.expresslog.Counter;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -7650,13 +7651,6 @@
| FLAG_GRANT_PREFIX_URI_PERMISSION;
/**
- * Flags that are not normally set by application code, but set for you by the system.
- */
- private static final int SYSTEM_ONLY_FLAGS = FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
- | FLAG_ACTIVITY_BROUGHT_TO_FRONT
- | FLAG_RECEIVER_FROM_SHELL;
-
- /**
* Local flag indicating this instance was created by copy constructor.
*/
private static final int LOCAL_FLAG_FROM_COPY = 1 << 0;
@@ -7709,11 +7703,6 @@
@TestApi
public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0;
- /**
- * Extended flags that are not normally set by application code, but set for you by the system.
- */
- private static final int SYSTEM_ONLY_EXTENDED_FLAGS = EXTENDED_FLAG_FILTER_MISMATCH;
-
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// toUri() and parseUri() options.
@@ -12657,28 +12646,6 @@
}
}
- /**
- * Prepare this {@link Intent} to enter system_server.
- *
- * @hide
- */
- public void prepareToEnterSystemServer() {
- // Refuse possible leaked file descriptors
- if (hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- // These flags are set only by the system, and should be stripped out as soon as the intent
- // is received by system_server from the caller so it can be properly updated later.
- removeFlags(SYSTEM_ONLY_FLAGS);
- removeExtendedFlags(SYSTEM_ONLY_EXTENDED_FLAGS);
- if (mOriginalIntent != null) {
- mOriginalIntent.prepareToEnterSystemServer();
- }
- if (mSelector != null) {
- mSelector.prepareToEnterSystemServer();
- }
- }
-
/** @hide */
public boolean hasWebURI() {
if (getData() == null) {
@@ -12839,6 +12806,8 @@
new ClipData.Item(text, htmlText, null, stream));
setClipData(clipData);
if (stream != null) {
+ logCounterIfFlagsMissing(FLAG_GRANT_READ_URI_PERMISSION,
+ "intents.value_explicit_uri_grant_for_send_action");
addFlags(FLAG_GRANT_READ_URI_PERMISSION);
}
return true;
@@ -12880,6 +12849,8 @@
setClipData(clipData);
if (streams != null) {
+ logCounterIfFlagsMissing(FLAG_GRANT_READ_URI_PERMISSION,
+ "intents.value_explicit_uri_grant_for_send_multiple_action");
addFlags(FLAG_GRANT_READ_URI_PERMISSION);
}
return true;
@@ -12899,6 +12870,10 @@
putExtra(MediaStore.EXTRA_OUTPUT, output);
setClipData(ClipData.newRawUri("", output));
+
+ logCounterIfFlagsMissing(
+ FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_READ_URI_PERMISSION,
+ "intents.value_explicit_uri_grant_for_image_capture_action");
addFlags(FLAG_GRANT_WRITE_URI_PERMISSION|FLAG_GRANT_READ_URI_PERMISSION);
return true;
}
@@ -12907,6 +12882,12 @@
return false;
}
+ private void logCounterIfFlagsMissing(int requiredFlags, String metricId) {
+ if ((getFlags() & requiredFlags) != requiredFlags) {
+ Counter.logIncrement(metricId);
+ }
+ }
+
@android.ravenwood.annotation.RavenwoodThrow
private Uri maybeConvertFileToContentUri(Context context, Uri uri) {
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index e895d7b..60d1b0d 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -3141,6 +3141,14 @@
for (int i = 0; i < N; i++) {
mUriRelativeFilterGroups.add(new UriRelativeFilterGroup(source));
}
+ if (source.dataAvail() > 0) {
+ Log.e(TAG, "Parcel data not fully consumed after completed reading"
+ + " UriRelativeFilterGroup data");
+ }
+ }
+ if (source.dataAvail() > 0) {
+ Log.e(TAG, "Parcel data not fully consumed when unparceling intent filter",
+ new Exception());
}
}
diff --git a/core/java/android/content/UriRelativeFilterGroup.java b/core/java/android/content/UriRelativeFilterGroup.java
index 0e49b4f..07aeb26 100644
--- a/core/java/android/content/UriRelativeFilterGroup.java
+++ b/core/java/android/content/UriRelativeFilterGroup.java
@@ -63,6 +63,7 @@
*/
@FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
public final class UriRelativeFilterGroup {
+ private static final String TAG = "UriRelativeFilterGroup";
private static final String ALLOW_STR = "allow";
private static final String URI_RELATIVE_FILTER_GROUP_STR = "uriRelativeFilterGroup";
@@ -233,9 +234,16 @@
final int n = mUriRelativeFilters.size();
if (n > 0) {
dest.writeInt(n);
+ int i = 0;
Iterator<UriRelativeFilter> it = mUriRelativeFilters.iterator();
while (it.hasNext()) {
it.next().writeToParcel(dest, flags);
+ i++;
+ }
+ if (i != n) {
+ Log.e(TAG, "UriRelativeFilters was unexpectedly"
+ + " modified while writing to parcel. Expected "
+ + n + " but found " + i + " filters", new Exception());
}
} else {
dest.writeInt(0);
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 495ae60..34bea1a 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -849,6 +849,12 @@
*/
public static final int PRIVATE_FLAG_EXT_CPU_OVERRIDE = 1 << 5;
+ /**
+ * Whether the app has been previously not launched
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EXT_NOT_LAUNCHED = 1 << 6;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = {
PRIVATE_FLAG_EXT_PROFILEABLE,
@@ -857,6 +863,7 @@
PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK,
PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS,
PRIVATE_FLAG_EXT_CPU_OVERRIDE,
+ PRIVATE_FLAG_EXT_NOT_LAUNCHED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlagsExt {}
@@ -2664,6 +2671,22 @@
}
/**
+ * Returns whether the app in the STOPPED state.
+ * @hide
+ */
+ public boolean isStopped() {
+ return (flags & ApplicationInfo.FLAG_STOPPED) != 0;
+ }
+
+ /**
+ * Returns whether the app was never launched (any process started) before.
+ * @hide
+ */
+ public boolean isNotLaunched() {
+ return (privateFlagsExt & ApplicationInfo.PRIVATE_FLAG_EXT_NOT_LAUNCHED) != 0;
+ }
+
+ /**
* Checks if a changeId is enabled for the current user
* @param changeId The changeId to verify
* @return True of the changeId is enabled
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index cb3455b..bb91a37 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -43,9 +43,11 @@
private final PackageManager mPm;
private final LauncherActivityInfoInternal mInternal;
- private static final UnicodeSet TRIMMABLE_CHARACTERS =
+ private static final UnicodeSet INVISIBLE_CHARACTERS =
new UnicodeSet("[[:White_Space:][:Default_Ignorable_Code_Point:][:gc=Cc:]]",
/* ignoreWhitespace= */ false).freeze();
+ // Only allow 3 consecutive invisible characters in the prefix of the string.
+ private static final int PREFIX_CONSECUTIVE_INVISIBLE_CHARACTERS_MAXIMUM = 3;
/**
* Create a launchable activity object for a given ResolveInfo and user.
@@ -93,17 +95,21 @@
return getActivityInfo().loadLabel(mPm);
}
- CharSequence label = trim(getActivityInfo().loadLabel(mPm));
- // If the trimmed label is empty, use application's label instead
- if (TextUtils.isEmpty(label)) {
- label = trim(getApplicationInfo().loadLabel(mPm));
- // If the trimmed label is still empty, use package name instead
- if (TextUtils.isEmpty(label)) {
- label = getComponentName().getPackageName();
- }
+ CharSequence label = getActivityInfo().loadLabel(mPm).toString().trim();
+ // If the activity label is visible to the user, return the original activity label
+ if (isVisible(label)) {
+ return label;
}
- // TODO: Go through LauncherAppsService
- return label;
+
+ // Use application label instead
+ label = getApplicationInfo().loadLabel(mPm).toString().trim();
+ // If the application label is visible to the user, return the original application label
+ if (isVisible(label)) {
+ return label;
+ }
+
+ // Use package name instead
+ return getComponentName().getPackageName();
}
/**
@@ -207,147 +213,75 @@
}
/**
- * If the {@code ch} is trimmable, return {@code true}. Otherwise, return
- * {@code false}. If the count of the code points of {@code ch} doesn't
- * equal 1, return {@code false}.
+ * Check whether the {@code sequence} is visible to the user or not.
* <p>
- * There are two types of the trimmable characters.
- * 1. The character is one of the Default_Ignorable_Code_Point in
+ * Return {@code false} when one of these conditions are satisfied:
+ * 1. The {@code sequence} starts with at least consecutive three invisible characters.
+ * 2. The sequence is composed of the invisible characters and non-glyph characters.
+ * <p>
+ * Invisible character is one of the Default_Ignorable_Code_Point in
* <a href="
* https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt">
* DerivedCoreProperties.txt</a>, the White_Space in <a href=
* "https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt">PropList.txt
* </a> or category Cc.
* <p>
- * 2. The character is not supported in the current system font.
+ * Non-glyph character means the character is not supported in the current system font.
* {@link android.graphics.Paint#hasGlyph(String)}
* <p>
*
+ * @hide
*/
- private static boolean isTrimmable(@NonNull Paint paint, @NonNull CharSequence ch) {
- Objects.requireNonNull(paint);
- Objects.requireNonNull(ch);
-
- // if ch is empty or it is not a character (i,e, the count of code
- // point doesn't equal one), return false
- if (TextUtils.isEmpty(ch)
- || Character.codePointCount(ch, /* beginIndex= */ 0, ch.length()) != 1) {
+ @VisibleForTesting
+ public static boolean isVisible(@NonNull CharSequence sequence) {
+ Objects.requireNonNull(sequence);
+ if (TextUtils.isEmpty(sequence)) {
return false;
}
- // Return true for the cases as below:
- // 1. The character is in the TRIMMABLE_CHARACTERS set
- // 2. The character is not supported in the system font
- return TRIMMABLE_CHARACTERS.contains(ch) || !paint.hasGlyph(ch.toString());
- }
-
- /**
- * If the {@code sequence} has some leading trimmable characters, creates a new copy
- * and removes the trimmable characters from the copy. Otherwise the given
- * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
- * to determine whether the character is trimmable or not.
- *
- * @return the trimmed string or the original string that has no
- * leading trimmable characters.
- * @see #isTrimmable(Paint, CharSequence)
- * @see #trim(CharSequence)
- * @see #trimEnd(CharSequence)
- *
- * @hide
- */
- @VisibleForTesting
- @NonNull
- public static CharSequence trimStart(@NonNull CharSequence sequence) {
- Objects.requireNonNull(sequence);
-
- if (TextUtils.isEmpty(sequence)) {
- return sequence;
- }
-
final Paint paint = new Paint();
- int trimCount = 0;
+ int invisibleCharCount = 0;
+ int notSupportedCharCount = 0;
final int[] codePoints = sequence.codePoints().toArray();
for (int i = 0, length = codePoints.length; i < length; i++) {
String ch = new String(new int[]{codePoints[i]}, /* offset= */ 0, /* count= */ 1);
- if (!isTrimmable(paint, ch)) {
- break;
+
+ // The check steps:
+ // 1. If the character is contained in INVISIBLE_CHARACTERS, invisibleCharCount++.
+ // 1.1 Check whether the invisibleCharCount is larger or equal to
+ // PREFIX_INVISIBLE_CHARACTERS_MAXIMUM when notSupportedCharCount is zero.
+ // It means that there are three consecutive invisible characters at the
+ // start of the string, return false.
+ // Otherwise, continue.
+ // 2. If the character is not supported on the system:
+ // notSupportedCharCount++, continue
+ // 3. If it does not continue or return on the above two cases, it means the
+ // character is visible and supported on the system, break.
+ // After going through the whole string, if the sum of invisibleCharCount
+ // and notSupportedCharCount is smaller than the length of the string, it
+ // means the string has the other visible characters, return true.
+ // Otherwise, return false.
+ if (INVISIBLE_CHARACTERS.contains(ch)) {
+ invisibleCharCount++;
+ // If there are three successive invisible characters at the start of the
+ // string, it is hard to visible to the user.
+ if (notSupportedCharCount == 0
+ && invisibleCharCount >= PREFIX_CONSECUTIVE_INVISIBLE_CHARACTERS_MAXIMUM) {
+ return false;
+ }
+ continue;
}
- trimCount += ch.length();
- }
- if (trimCount == 0) {
- return sequence;
- }
- return sequence.subSequence(trimCount, sequence.length());
- }
- /**
- * If the {@code sequence} has some trailing trimmable characters, creates a new copy
- * and removes the trimmable characters from the copy. Otherwise the given
- * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
- * to determine whether the character is trimmable or not.
- *
- * @return the trimmed sequence or the original sequence that has no
- * trailing trimmable characters.
- * @see #isTrimmable(Paint, CharSequence)
- * @see #trimStart(CharSequence)
- * @see #trim(CharSequence)
- *
- * @hide
- */
- @VisibleForTesting
- @NonNull
- public static CharSequence trimEnd(@NonNull CharSequence sequence) {
- Objects.requireNonNull(sequence);
-
- if (TextUtils.isEmpty(sequence)) {
- return sequence;
- }
-
- final Paint paint = new Paint();
- int trimCount = 0;
- final int[] codePoints = sequence.codePoints().toArray();
- for (int i = codePoints.length - 1; i >= 0; i--) {
- String ch = new String(new int[]{codePoints[i]}, /* offset= */ 0, /* count= */ 1);
- if (!isTrimmable(paint, ch)) {
- break;
+ // The character is not supported on the system, but it may not be an invisible
+ // character. E.g. tofu (a rectangle).
+ if (!paint.hasGlyph(ch)) {
+ notSupportedCharCount++;
+ continue;
}
- trimCount += ch.length();
+ // The character is visible and supported on the system, break the for loop
+ break;
}
- if (trimCount == 0) {
- return sequence;
- }
- return sequence.subSequence(0, sequence.length() - trimCount);
- }
-
- /**
- * If the {@code sequence} has some leading or trailing trimmable characters, creates
- * a new copy and removes the trimmable characters from the copy. Otherwise the given
- * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
- * to determine whether the character is trimmable or not.
- *
- * @return the trimmed sequence or the original sequence that has no leading or
- * trailing trimmable characters.
- * @see #isTrimmable(Paint, CharSequence)
- * @see #trimStart(CharSequence)
- * @see #trimEnd(CharSequence)
- *
- * @hide
- */
- @VisibleForTesting
- @NonNull
- public static CharSequence trim(@NonNull CharSequence sequence) {
- Objects.requireNonNull(sequence);
-
- if (TextUtils.isEmpty(sequence)) {
- return sequence;
- }
-
- CharSequence result = trimStart(sequence);
- if (TextUtils.isEmpty(result)) {
- return result;
- }
-
- return trimEnd(result);
+ return (invisibleCharCount + notSupportedCharCount < codePoints.length);
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8c56a9d..26bb6e4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8847,6 +8847,8 @@
} catch (PackageParserException e) {
Log.w(TAG, "Failure to parse package archive apkFile= " +apkFile);
return null;
+ } finally {
+ parser2.close();
}
}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 3a33ef9..9eec7a4 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -161,6 +161,16 @@
}
flag {
+ name: "fix_avatar_content_provider_null_authority"
+ namespace: "multiuser"
+ description: "Fix crash when content provider authority is null."
+ bug: "362880068"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fix_avatar_picker_not_responding_for_new_user"
namespace: "multiuser"
description: "Avatar picker is not responding after selecting photo for new user."
@@ -416,4 +426,13 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+
+flag {
+ name: "caching_development_improvements"
+ namespace: "multiuser"
+ description: "System API to simplify caching implamentations"
+ bug: "364947162"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index d128055..a20159d 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -19,8 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
-import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Parcel;
@@ -136,11 +134,8 @@
* Get rollback impact level. Refer {@link
* android.content.pm.PackageInstaller.SessionParams#setRollbackImpactLevel(int)} for more info
* on impact level.
- *
- * @hide
*/
- @TestApi
- @FlaggedApi(Flags.FLAG_RECOVERABILITY_DETECTION)
+ @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
public @PackageManager.RollbackImpactLevel int getRollbackImpactLevel() {
return mRollbackImpactLevel;
}
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 6514872..ef59e0a 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -26,6 +26,10 @@
import android.database.sqlite.SQLiteException;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodThrow;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
@@ -40,9 +44,8 @@
* consumer for reading.
* </p>
*/
[email protected]
[email protected](
- "com.android.platform.test.ravenwood.nativesubstitution.CursorWindow_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("CursorWindow_host")
public class CursorWindow extends SQLiteClosable implements Parcelable {
private static final String STATS_TAG = "CursorWindowStats";
@@ -63,48 +66,69 @@
private final CloseGuard mCloseGuard;
// May throw CursorWindowAllocationException
+ @RavenwoodRedirect
private static native long nativeCreate(String name, int cursorWindowSize);
// May throw CursorWindowAllocationException
+ @RavenwoodRedirect
private static native long nativeCreateFromParcel(Parcel parcel);
+ @RavenwoodRedirect
private static native void nativeDispose(long windowPtr);
+ @RavenwoodRedirect
private static native void nativeWriteToParcel(long windowPtr, Parcel parcel);
+ @RavenwoodRedirect
private static native String nativeGetName(long windowPtr);
+ @RavenwoodRedirect
private static native byte[] nativeGetBlob(long windowPtr, int row, int column);
+ @RavenwoodRedirect
private static native String nativeGetString(long windowPtr, int row, int column);
+ @RavenwoodThrow
private static native void nativeCopyStringToBuffer(long windowPtr, int row, int column,
CharArrayBuffer buffer);
+ @RavenwoodRedirect
private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column);
+ @RavenwoodRedirect
private static native boolean nativePutString(long windowPtr, String value,
int row, int column);
// Below native methods don't do unconstrained work, so are FastNative for performance
@FastNative
+ @RavenwoodThrow
private static native void nativeClear(long windowPtr);
@FastNative
+ @RavenwoodRedirect
private static native int nativeGetNumRows(long windowPtr);
@FastNative
+ @RavenwoodRedirect
private static native boolean nativeSetNumColumns(long windowPtr, int columnNum);
@FastNative
+ @RavenwoodRedirect
private static native boolean nativeAllocRow(long windowPtr);
@FastNative
+ @RavenwoodThrow
private static native void nativeFreeLastRow(long windowPtr);
@FastNative
+ @RavenwoodRedirect
private static native int nativeGetType(long windowPtr, int row, int column);
@FastNative
+ @RavenwoodRedirect
private static native long nativeGetLong(long windowPtr, int row, int column);
@FastNative
+ @RavenwoodRedirect
private static native double nativeGetDouble(long windowPtr, int row, int column);
@FastNative
+ @RavenwoodRedirect
private static native boolean nativePutLong(long windowPtr, long value, int row, int column);
@FastNative
+ @RavenwoodRedirect
private static native boolean nativePutDouble(long windowPtr, double value, int row, int column);
@FastNative
+ @RavenwoodThrow
private static native boolean nativePutNull(long windowPtr, int row, int column);
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index f54be00..60fd0ce 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -236,15 +236,21 @@
*
* {@more} Note that the value of this flag is 0, so it is the default.
*/
- public static final int OPEN_READWRITE = 0x00000000; // update native code if changing
+ // LINT.IfChange
+ public static final int OPEN_READWRITE = 0x00000000;
+ // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)
/**
* Open flag: Flag for {@link #openDatabase} to open the database for reading only.
* This is the only reliable way to open a database if the disk may be full.
*/
- public static final int OPEN_READONLY = 0x00000001; // update native code if changing
+ // LINT.IfChange
+ public static final int OPEN_READONLY = 0x00000001;
+ // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)
- private static final int OPEN_READ_MASK = 0x00000001; // update native code if changing
+ // LINT.IfChange
+ private static final int OPEN_READ_MASK = 0x00000001;
+ // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)
/**
* Open flag: Flag for {@link #openDatabase} to open the database without support for
@@ -254,13 +260,31 @@
* You must be consistent when using this flag to use the setting the database was
* created with. If this is set, {@link #setLocale} will do nothing.
*/
- public static final int NO_LOCALIZED_COLLATORS = 0x00000010; // update native code if changing
+ // LINT.IfChange
+ public static final int NO_LOCALIZED_COLLATORS = 0x00000010;
+ // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)
+
+ /**
+ * Open flag: Flag for {@link #openDatabase} to open a database, disallowing double-quoted
+ * strings.
+ *
+ * This causes sqlite to reject SQL statements with double-quoted string literals. String
+ * literals must be enclosed in single quotes; double-quotes are reserved for identifiers like
+ * column names.
+ * See https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted
+ * @hide
+ */
+ // LINT.IfChange
+ public static final int NO_DOUBLE_QUOTED_STRS = 0x00000020;
+ // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)
/**
* Open flag: Flag for {@link #openDatabase} to create the database file if it does not
* already exist.
*/
- public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
+ // LINT.IfChange
+ public static final int CREATE_IF_NECESSARY = 0x10000000;
+ // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)
/**
* Open flag: Flag for {@link #openDatabase} to open the database file with
@@ -490,6 +514,9 @@
if (SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()) {
mConfigurationLocked.openFlags |= ENABLE_LEGACY_COMPATIBILITY_WAL;
}
+ if (SQLiteDebug.NoPreloadHolder.NO_DOUBLE_QUOTED_STRS) {
+ mConfigurationLocked.openFlags |= NO_DOUBLE_QUOTED_STRS;
+ }
mConfigurationLocked.journalMode = journalMode;
mConfigurationLocked.syncMode = syncMode;
}
@@ -3275,6 +3302,7 @@
OPEN_READONLY,
CREATE_IF_NECESSARY,
NO_LOCALIZED_COLLATORS,
+ NO_DOUBLE_QUOTED_STRS,
ENABLE_WRITE_AHEAD_LOGGING
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 93d74b1..b648e05 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -66,7 +66,6 @@
public static final boolean DEBUG_SQL_TIME =
Log.isLoggable("SQLiteTime", Log.VERBOSE);
-
/**
* True to enable database performance testing instrumentation.
*/
@@ -83,6 +82,15 @@
*/
public static final boolean DEBUG_LOG_DETAILED = Build.IS_DEBUGGABLE
&& SystemProperties.getBoolean("db.log.detailed", false);
+
+ /**
+ * Whether to accept double-quoted strings in SQL statements. Double-quoted strings are a
+ * syntax error but are accepted by sqlite in compatibility mode (the default). If the
+ * property is set to true, double-quoted strings will be treated by sqlite as a syntax
+ * error.
+ */
+ public static final boolean NO_DOUBLE_QUOTED_STRS =
+ SystemProperties.getBoolean("debug.sqlite.no_double_quoted_strs", false);
}
private SQLiteDebug() {
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 8975191..9355937 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -170,6 +170,12 @@
int BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE = 20;
/**
+ * Biometrics is not allowed to verify in apps.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS = 21;
+
+ /**
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
* because the authentication attempt was unsuccessful.
* @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index fc72db3..6682b36 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -61,7 +61,6 @@
BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
BIOMETRIC_ERROR_RE_ENROLL,
- BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
FINGERPRINT_ERROR_UNKNOWN,
FINGERPRINT_ERROR_BAD_CALIBRATION,
BIOMETRIC_ERROR_POWER_PRESSED})
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 9bc46b9..a4f7485f 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -94,6 +94,13 @@
BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
/**
+ * Biometrics is not allowed to verify in apps.
+ * @hide
+ */
+ public static final int BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS =
+ BiometricConstants.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS;
+
+ /**
* A security vulnerability has been discovered and the sensor is unavailable until a
* security update has addressed this issue. This error can be received if for example,
* authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index fbed50a..ac72116 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1537,9 +1537,14 @@
* be made, and for firing pre-capture flash pulses to estimate
* scene brightness and required final capture flash power, when
* the flash is enabled.</p>
- * <p>Normally, this entry should be set to START for only a
- * single request, and the application should wait until the
- * sequence completes before starting a new one.</p>
+ * <p>Flash is enabled during precapture sequence when:</p>
+ * <ul>
+ * <li>AE mode is ON_ALWAYS_FLASH</li>
+ * <li>AE mode is ON_AUTO_FLASH and the scene is deemed too dark without flash, or</li>
+ * <li>AE mode is ON and flash mode is TORCH or SINGLE</li>
+ * </ul>
+ * <p>Normally, this entry should be set to START for only single request, and the
+ * application should wait until the sequence completes before starting a new one.</p>
* <p>When a precapture metering sequence is finished, the camera device
* may lock the auto-exposure routine internally to be able to accurately expose the
* subsequent still capture image (<code>{@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE</code>).
@@ -2705,6 +2710,13 @@
* in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
* If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
* <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
+ * <p>When AE mode is ON and flash mode is TORCH or SINGLE, the application should make sure
+ * the AE mode, flash mode, and flash strength level remain the same between precapture
+ * trigger request and final capture request. The flash strength level being set during
+ * precapture sequence is used by the camera device as a reference. The actual strength
+ * may be less, and the auto-exposure routine makes sure proper conversions of sensor
+ * exposure time and sensitivities between precapture and final capture for the specified
+ * strength level.</p>
* <p><b>Range of valid values:</b><br>
* <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
* set to TORCH;
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index d652b4c..34ce92c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -935,9 +935,14 @@
* be made, and for firing pre-capture flash pulses to estimate
* scene brightness and required final capture flash power, when
* the flash is enabled.</p>
- * <p>Normally, this entry should be set to START for only a
- * single request, and the application should wait until the
- * sequence completes before starting a new one.</p>
+ * <p>Flash is enabled during precapture sequence when:</p>
+ * <ul>
+ * <li>AE mode is ON_ALWAYS_FLASH</li>
+ * <li>AE mode is ON_AUTO_FLASH and the scene is deemed too dark without flash, or</li>
+ * <li>AE mode is ON and flash mode is TORCH or SINGLE</li>
+ * </ul>
+ * <p>Normally, this entry should be set to START for only single request, and the
+ * application should wait until the sequence completes before starting a new one.</p>
* <p>When a precapture metering sequence is finished, the camera device
* may lock the auto-exposure routine internally to be able to accurately expose the
* subsequent still capture image (<code>{@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE</code>).
@@ -2821,8 +2826,6 @@
* boost when the light level threshold is exceeded.</p>
* <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
* indicate when it is not being applied by returning 'INACTIVE'.</p>
- * <p>This key will be absent from the CaptureResult if AE mode is not set to
- * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
* <p>The default value will always be 'INACTIVE'.</p>
* <p><b>Possible values:</b></p>
* <ul>
@@ -2996,6 +2999,13 @@
* in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
* If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
* <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
+ * <p>When AE mode is ON and flash mode is TORCH or SINGLE, the application should make sure
+ * the AE mode, flash mode, and flash strength level remain the same between precapture
+ * trigger request and final capture request. The flash strength level being set during
+ * precapture sequence is used by the camera device as a reference. The actual strength
+ * may be less, and the auto-exposure routine makes sure proper conversions of sensor
+ * exposure time and sensitivities between precapture and final capture for the specified
+ * strength level.</p>
* <p><b>Range of valid values:</b><br>
* <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
* set to TORCH;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index a60c48e..8407258 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -80,6 +80,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -90,6 +91,17 @@
private final String TAG;
private final boolean DEBUG = false;
+ private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+ private static final ThreadFactory mFactory = Executors.defaultThreadFactory();
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = mFactory.newThread(r);
+ thread.setName("CameraDeviceExecutor");
+ return thread;
+ }
+ };
+
private static final int REQUEST_ID_NONE = -1;
/**
@@ -352,12 +364,11 @@
throw new IllegalArgumentException("Null argument given");
}
mCameraId = cameraId;
- if (Flags.singleThreadExecutor()) {
- mDeviceCallback = new ClientStateCallback(executor, callback);
- mDeviceExecutor = Executors.newSingleThreadExecutor();
+ mDeviceCallback = new ClientStateCallback(executor, callback);
+ if (Flags.singleThreadExecutorNaming()) {
+ mDeviceExecutor = Executors.newSingleThreadExecutor(sThreadFactory);
} else {
- mDeviceCallback = callback;
- mDeviceExecutor = executor;
+ mDeviceExecutor = Executors.newSingleThreadExecutor();
}
mCharacteristics = characteristics;
mCameraManager = manager;
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 109b0a8..6a96a54 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -158,6 +158,8 @@
return "thermal";
case BRIGHTNESS_MAX_REASON_POWER_IC:
return "power IC";
+ case BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE:
+ return "wear bedtime";
}
return "invalid";
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
index 43c0da9..48c5887 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
@@ -23,12 +23,14 @@
import android.content.Context;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.biometrics.fingerprint.virtualhal.IVirtualHal;
import android.os.Binder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.HashMap;
@@ -162,6 +164,43 @@
dest.writeMap(mSensorPropsMap);
}
+
+ /**
+ * Remap fqName of VHAL because the `virtual` instance is registered
+ * with IVirtulalHal now (IFingerprint previously)
+ * @param fqName fqName to be translated
+ * @return real fqName
+ */
+ public static String remapFqName(String fqName) {
+ if (!fqName.contains(IFingerprint.DESCRIPTOR + "/virtual")) {
+ return fqName; //no remap needed for real hardware HAL
+ } else {
+ //new Vhal instance name
+ return fqName.replace("IFingerprint", "virtualhal.IVirtualHal");
+ }
+ }
+
+ /**
+ * @param fqName aidl interface instance name
+ * @return aidl interface
+ */
+ public static IFingerprint getIFingerprint(String fqName) {
+ if (fqName.contains("virtual")) {
+ String fqNameMapped = remapFqName(fqName);
+ Slog.i(TAG, "getIFingerprint fqName is mapped: " + fqName + "->" + fqNameMapped);
+ try {
+ IVirtualHal vhal = IVirtualHal.Stub.asInterface(
+ Binder.allowBlocking(ServiceManager.waitForService(fqNameMapped)));
+ return vhal.getFingerprintHal();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in vhal.getFingerprintHal() call" + fqNameMapped);
+ }
+ }
+
+ return IFingerprint.Stub.asInterface(
+ Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+ }
+
/**
* Returns fingerprint sensor props for the HAL {@param instance}.
*/
@@ -176,8 +215,7 @@
try {
final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
- final IFingerprint fp = IFingerprint.Stub.asInterface(Binder.allowBlocking(
- ServiceManager.waitForDeclaredService(fqName)));
+ final IFingerprint fp = getIFingerprint(fqName);
if (fp != null) {
props = fp.getSensorProps();
} else {
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/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 03cf7c5..2a36238 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -567,7 +567,7 @@
Objects.requireNonNull(listener, "listener must not be null");
synchronized (mOnTabletModeChangedListeners) {
- if (mOnTabletModeChangedListeners == null) {
+ if (mOnTabletModeChangedListeners.isEmpty()) {
initializeTabletModeListenerLocked();
}
int idx = findOnTabletModeChangedListenerLocked(listener);
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index c5d0caf22..8592ded 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -20,10 +20,12 @@
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS;
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG;
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG;
+import static com.android.hardware.input.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11ySlowKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
+import static com.android.hardware.input.Flags.keyboardRepeatKeys;
import static com.android.hardware.input.Flags.touchpadTapDragging;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.input.flags.Flags.enableInputFilterRustImpl;
@@ -40,6 +42,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.sysprop.InputProperties;
+import android.view.ViewConfiguration;
/**
* InputSettings encapsulates reading and writing settings related to input
@@ -90,6 +93,30 @@
*/
public static final int DEFAULT_STYLUS_POINTER_ICON_ENABLED = 1;
+ /**
+ * The minimum allowed repeat keys timeout before starting key repeats.
+ * @hide
+ */
+ public static final int MIN_KEY_REPEAT_TIMEOUT_MILLIS = 150;
+
+ /**
+ * The maximum allowed repeat keys timeout before starting key repeats.
+ * @hide
+ */
+ public static final int MAX_KEY_REPEAT_TIMEOUT_MILLIS = 2000;
+
+ /**
+ * The minimum allowed repeat keys delay between successive key repeats.
+ * @hide
+ */
+ public static final int MIN_KEY_REPEAT_DELAY_MILLIS = 20;
+
+ /**
+ * The maximum allowed repeat keys delay between successive key repeats.
+ * @hide
+ */
+ public static final int MAX_KEY_REPEAT_DELAY_MILLIS = 2000;
+
private InputSettings() {
}
@@ -767,4 +794,141 @@
Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
}
+
+ /**
+ * Whether "Repeat keys" feature flag is enabled.
+ *
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ public static boolean isRepeatKeysFeatureFlagEnabled() {
+ return keyboardRepeatKeys();
+ }
+
+ /**
+ * Get Accessibility repeat keys timeout duration in milliseconds.
+ * The default key repeat timeout is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_TIMEOUT_MS}.
+ *
+ * @param context The application context
+ * @return The time duration for which a key should be pressed after
+ * which the pressed key will be repeated. The timeout must be between
+ * {@link #MIN_KEY_REPEAT_TIMEOUT_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_TIMEOUT_MILLIS}
+ *
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ public static int getAccessibilityRepeatKeysTimeout(@NonNull Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS, ViewConfiguration.getKeyRepeatTimeout(),
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Get Accessibility repeat keys delay rate in milliseconds.
+ * The default key repeat delay is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_DELAY_MS}.
+ *
+ * @param context The application context
+ * @return Time duration between successive key repeats when a key is
+ * pressed down. The delay duration must be between
+ * {@link #MIN_KEY_REPEAT_DELAY_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_DELAY_MILLIS}
+ *
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ public static int getAccessibilityRepeatKeysDelay(@NonNull Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(),
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Set Accessibility repeat keys timeout duration in milliseconds.
+ *
+ * @param timeoutTimeMillis time duration for which a key should be pressed after which the
+ * pressed key will be repeated. The timeout must be between
+ * {@link #MIN_KEY_REPEAT_TIMEOUT_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_TIMEOUT_MILLIS}
+ *
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setAccessibilityRepeatKeysTimeout(@NonNull Context context,
+ int timeoutTimeMillis) {
+ if (timeoutTimeMillis < MIN_KEY_REPEAT_TIMEOUT_MILLIS
+ || timeoutTimeMillis > MAX_KEY_REPEAT_TIMEOUT_MILLIS) {
+ throw new IllegalArgumentException(
+ "Provided repeat keys timeout should be in range ("
+ + MIN_KEY_REPEAT_TIMEOUT_MILLIS + ","
+ + MAX_KEY_REPEAT_TIMEOUT_MILLIS + ")");
+ }
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS, timeoutTimeMillis,
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Set Accessibility repeat key delay duration in milliseconds.
+ *
+ * @param delayTimeMillis Time duration between successive key repeats when a key is
+ * pressed down. The delay duration must be between
+ * {@link #MIN_KEY_REPEAT_DELAY_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_DELAY_MILLIS}
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setAccessibilityRepeatKeysDelay(@NonNull Context context,
+ int delayTimeMillis) {
+ if (delayTimeMillis < MIN_KEY_REPEAT_DELAY_MILLIS
+ || delayTimeMillis > MAX_KEY_REPEAT_DELAY_MILLIS) {
+ throw new IllegalArgumentException(
+ "Provided repeat keys delay should be in range ("
+ + MIN_KEY_REPEAT_DELAY_MILLIS + ","
+ + MAX_KEY_REPEAT_DELAY_MILLIS + ")");
+ }
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_DELAY_MS, delayTimeMillis,
+ UserHandle.USER_CURRENT);
+ }
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 83c4de3..4478592 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -99,3 +99,28 @@
description: "Refactor ModifierShortcutManager internal representation of shortcuts."
bug: "358603902"
}
+
+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_native"
+ description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
+ bug: "336585002"
+}
+
+flag {
+ name: "mouse_reverse_vertical_scrolling"
+ namespace: "input"
+ description: "Controls whether external mouse vertical scrolling can be reversed"
+ bug: "352598211"
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 3a58993..218b023 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -256,6 +256,10 @@
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int[] getContextHubHandles() {
+ if (Flags.removeOldContextHubApis()) {
+ return null;
+ }
+
try {
return mService.getContextHubHandles();
} catch (RemoteException e) {
@@ -277,6 +281,10 @@
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public ContextHubInfo getContextHubInfo(int hubHandle) {
+ if (Flags.removeOldContextHubApis()) {
+ return null;
+ }
+
try {
return mService.getContextHubInfo(hubHandle);
} catch (RemoteException e) {
@@ -308,6 +316,10 @@
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int loadNanoApp(int hubHandle, @NonNull NanoApp app) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
try {
return mService.loadNanoApp(hubHandle, app);
} catch (RemoteException e) {
@@ -335,6 +347,10 @@
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int unloadNanoApp(int nanoAppHandle) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
try {
return mService.unloadNanoApp(nanoAppHandle);
} catch (RemoteException e) {
@@ -375,6 +391,10 @@
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
+ if (Flags.removeOldContextHubApis()) {
+ return null;
+ }
+
try {
return mService.getNanoAppInstanceInfo(nanoAppHandle);
} catch (RemoteException e) {
@@ -398,6 +418,10 @@
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) {
+ if (Flags.removeOldContextHubApis()) {
+ return null;
+ }
+
try {
return mService.findNanoAppOnHub(hubHandle, filter);
} catch (RemoteException e) {
@@ -433,6 +457,10 @@
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
try {
return mService.sendMessage(hubHandle, nanoAppHandle, message);
} catch (RemoteException e) {
@@ -648,6 +676,10 @@
@Deprecated
@SuppressLint("RequiresPermission")
public int registerCallback(@NonNull Callback callback) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
return registerCallback(callback, null);
}
@@ -657,6 +689,10 @@
*/
@Deprecated
public int registerCallback(ICallback callback) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
if (mLocalCallback != null) {
Log.w(TAG, "Max number of local callbacks reached!");
return -1;
@@ -682,6 +718,10 @@
@Deprecated
@SuppressLint("RequiresPermission")
public int registerCallback(Callback callback, Handler handler) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
synchronized(this) {
if (mCallback != null) {
Log.w(TAG, "Max number of callbacks reached!");
@@ -1041,16 +1081,20 @@
@SuppressLint("RequiresPermission")
@Deprecated
public int unregisterCallback(@NonNull Callback callback) {
- synchronized(this) {
- if (callback != mCallback) {
- Log.w(TAG, "Cannot recognize callback!");
- return -1;
- }
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
- mCallback = null;
- mCallbackHandler = null;
- }
- return 0;
+ synchronized (this) {
+ if (callback != mCallback) {
+ Log.w(TAG, "Cannot recognize callback!");
+ return -1;
+ }
+
+ mCallback = null;
+ mCallbackHandler = null;
+ }
+ return 0;
}
/**
@@ -1059,6 +1103,10 @@
*/
@Deprecated
public synchronized int unregisterCallback(ICallback callback) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
if (callback != mLocalCallback) {
Log.w(TAG, "Cannot recognize local callback!");
return -1;
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index bfff4db..9f3e3ad 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -29,6 +29,7 @@
import static java.util.Objects.requireNonNull;
import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -874,10 +875,9 @@
/*****************************************************************************
* A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
* patterns.
- *
- * @hide
****************************************************************************/
- public static class GenericSoundModel extends SoundModel implements Parcelable {
+ @FlaggedApi(android.media.soundtrigger.Flags.FLAG_GENERIC_MODEL_API)
+ public static final class GenericSoundModel extends SoundModel implements Parcelable {
public static final @android.annotation.NonNull Parcelable.Creator<GenericSoundModel> CREATOR
= new Parcelable.Creator<GenericSoundModel>() {
@@ -890,11 +890,26 @@
}
};
+ /**
+ * Constructor for {@link GenericSoundModel} with version.
+ *
+ * @param uuid Unique identifier for this sound model.
+ * @param vendorUuid Unique vendor identifier for this sound model.
+ * @param data Opaque data for this sound model.
+ * @param version Vendor-specific version number of this sound model.
+ */
public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
@Nullable byte[] data, int version) {
super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version);
}
+ /**
+ * Constructor for {@link GenericSoundModel} without version. The version is set to -1.
+ *
+ * @param uuid Unique identifier for this sound model.
+ * @param vendorUuid Unique vendor identifier for this sound model.
+ * @param data Opaque data for this sound model.
+ */
@UnsupportedAppUsage
public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
@Nullable byte[] data) {
@@ -919,7 +934,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(getUuid().toString());
if (getVendorUuid() == null) {
dest.writeInt(-1);
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/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index d4d1ed2..dcb363c 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -55,14 +55,4 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
-
-flag{
- name: "allow_disable_ipsec_loss_detector"
- namespace: "vcn"
- description: "Allow disabling IPsec packet loss detector"
- bug: "336638836"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
}
\ No newline at end of file
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index 07fbe4a..0541a96 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -111,12 +111,15 @@
try {
int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote(
mAppInfo, mProcessInfo);
+
+ final int[] sharedAppGid = {
+ UserHandle.getSharedAppGid(UserHandle.getAppId(mAppInfo.uid)) };
mZygote = Process.ZYGOTE_PROCESS.startChildZygote(
"com.android.internal.os.AppZygoteInit",
mAppInfo.processName + "_zygote",
mZygoteUid,
mZygoteUid,
- null, // gids
+ sharedAppGid, // Zygote gets access to shared app GID for profiles
runtimeFlags,
"app_zygote", // seInfo
abi, // abi
diff --git a/core/java/android/os/ArtModuleServiceManager.java b/core/java/android/os/ArtModuleServiceManager.java
index e0b631d..995094b 100644
--- a/core/java/android/os/ArtModuleServiceManager.java
+++ b/core/java/android/os/ArtModuleServiceManager.java
@@ -37,10 +37,12 @@
/** A class that exposes the method to obtain each system service. */
public static final class ServiceRegisterer {
@NonNull private final String mServiceName;
+ private final boolean mRetry;
/** @hide */
- public ServiceRegisterer(@NonNull String serviceName) {
+ public ServiceRegisterer(@NonNull String serviceName, boolean retry) {
mServiceName = serviceName;
+ mRetry = retry;
}
/**
@@ -53,27 +55,47 @@
*/
@Nullable
public IBinder waitForService() {
- return ServiceManager.waitForService(mServiceName);
+ if (mRetry) {
+ return ServiceManager.waitForService(mServiceName);
+ }
+ IBinder binder = ServiceManager.getService(mServiceName);
+ for (int remainingTimeMs = 5000; binder == null && remainingTimeMs > 0;
+ remainingTimeMs -= 100) {
+ // There can be a race:
+ // 1. Client A invokes "ctl.start", which starts the service.
+ // 2. Client A gets a service handle from `ServiceManager.getService`.
+ // 3. Client B invokes "ctl.start", which does nothing because the service is
+ // already running.
+ // 4. Client A drops the service handle. The service is notified that there is no
+ // more client at that point, so it shuts down itself.
+ // 5. Client B cannot get a service handle from `ServiceManager.getService` because
+ // the service is shut down.
+ // To address this problem, we invoke "ctl.start" repeatedly.
+ SystemProperties.set("ctl.start", mServiceName);
+ SystemClock.sleep(100);
+ binder = ServiceManager.getService(mServiceName);
+ }
+ return binder;
}
}
/** Returns {@link ServiceRegisterer} for the "artd" service. */
@NonNull
public ServiceRegisterer getArtdServiceRegisterer() {
- return new ServiceRegisterer("artd");
+ return new ServiceRegisterer("artd", true /* retry */);
}
/** Returns {@link ServiceRegisterer} for the "artd_pre_reboot" service. */
@NonNull
@FlaggedApi(Flags.FLAG_USE_ART_SERVICE_V2)
public ServiceRegisterer getArtdPreRebootServiceRegisterer() {
- return new ServiceRegisterer("artd_pre_reboot");
+ return new ServiceRegisterer("artd_pre_reboot", false /* retry */);
}
/** Returns {@link ServiceRegisterer} for the "dexopt_chroot_setup" service. */
@NonNull
@FlaggedApi(Flags.FLAG_USE_ART_SERVICE_V2)
public ServiceRegisterer getDexoptChrootSetupServiceRegisterer() {
- return new ServiceRegisterer("dexopt_chroot_setup");
+ return new ServiceRegisterer("dexopt_chroot_setup", true /* retry */);
}
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 4bc3dbe..97e9f34 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -21,6 +21,8 @@
import android.annotation.SystemApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.ravenwood.annotation.RavenwoodClassLoadHook;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
@@ -30,11 +32,9 @@
import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.BinderInternal.CallSession;
-import com.android.internal.os.SomeArgs;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
-import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
@@ -48,7 +48,6 @@
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicReferenceArray;
-import java.util.function.Supplier;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -82,6 +81,8 @@
*
* @see IBinder
*/
+@RavenwoodKeepWholeClass
+@RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
public class Binder implements IBinder {
/*
* Set this flag to true to detect anonymous, local or member classes
@@ -292,33 +293,6 @@
sWarnOnBlockingOnCurrentThread.set(sWarnOnBlocking);
}
- private static volatile ThreadLocal<SomeArgs> sIdentity$ravenwood;
-
- @android.ravenwood.annotation.RavenwoodKeepWholeClass
- private static class IdentitySupplier implements Supplier<SomeArgs> {
- @Override
- public SomeArgs get() {
- final SomeArgs args = SomeArgs.obtain();
- // Match IPCThreadState behavior
- args.arg1 = Boolean.FALSE;
- args.argi1 = android.os.Process.myUid();
- args.argi2 = android.os.Process.myPid();
- return args;
- }
- }
-
- /** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
- public static void init$ravenwood() {
- sIdentity$ravenwood = ThreadLocal.withInitial(new IdentitySupplier());
- }
-
- /** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
- public static void reset$ravenwood() {
- sIdentity$ravenwood = null;
- }
-
/**
* Raw native pointer to JavaBBinderHolder object. Owned by this Java object. Not null.
*/
@@ -346,14 +320,8 @@
* 0 for a synchronous call.
*/
@CriticalNative
- @android.ravenwood.annotation.RavenwoodReplace
public static final native int getCallingPid();
- /** @hide */
- public static final int getCallingPid$ravenwood() {
- return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi2;
- }
-
/**
* Return the Linux UID assigned to the process that sent you the
* current transaction that is being processed. This UID can be used with
@@ -362,14 +330,8 @@
* incoming transaction, then its own UID is returned.
*/
@CriticalNative
- @android.ravenwood.annotation.RavenwoodReplace
public static final native int getCallingUid();
- /** @hide */
- public static final int getCallingUid$ravenwood() {
- return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi1;
- }
-
/**
* Returns {@code true} if the current thread is currently executing an
* incoming transaction.
@@ -377,21 +339,13 @@
* @hide
*/
@CriticalNative
- @android.ravenwood.annotation.RavenwoodReplace
public static final native boolean isDirectlyHandlingTransactionNative();
- /** @hide */
- public static final boolean isDirectlyHandlingTransactionNative$ravenwood() {
- // Ravenwood doesn't support IPC
- return false;
- }
-
private static boolean sIsHandlingBinderTransaction = false;
/**
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isDirectlyHandlingTransaction() {
return sIsHandlingBinderTransaction || isDirectlyHandlingTransactionNative();
}
@@ -400,7 +354,6 @@
* This is Test API which will be used to override output of isDirectlyHandlingTransactionNative
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static void setIsDirectlyHandlingTransactionOverride(boolean isInTransaction) {
sIsHandlingBinderTransaction = isInTransaction;
}
@@ -412,15 +365,8 @@
* @hide
*/
@CriticalNative
- @android.ravenwood.annotation.RavenwoodReplace
private static native boolean hasExplicitIdentity();
- /** @hide */
- private static boolean hasExplicitIdentity$ravenwood() {
- return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().arg1
- == Boolean.TRUE;
- }
-
/**
* Return the Linux UID assigned to the process that sent the transaction
* currently being processed.
@@ -429,7 +375,6 @@
* executing an incoming transaction and the calling identity has not been
* explicitly set with {@link #clearCallingIdentity()}
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static final int getCallingUidOrThrow() {
if (!isDirectlyHandlingTransaction() && !hasExplicitIdentity()) {
throw new IllegalStateException(
@@ -491,26 +436,8 @@
* @see #restoreCallingIdentity(long)
*/
@CriticalNative
- @android.ravenwood.annotation.RavenwoodReplace
public static final native long clearCallingIdentity();
- /** @hide */
- public static final long clearCallingIdentity$ravenwood() {
- final SomeArgs args = Preconditions.requireNonNullViaRavenwoodRule(
- sIdentity$ravenwood).get();
- long res = ((long) args.argi1 << 32) | args.argi2;
- if (args.arg1 == Boolean.TRUE) {
- res |= (0x1 << 30);
- } else {
- res &= ~(0x1 << 30);
- }
- // Match IPCThreadState behavior
- args.arg1 = Boolean.TRUE;
- args.argi1 = android.os.Process.myUid();
- args.argi2 = android.os.Process.myPid();
- return res;
- }
-
/**
* Restore the identity of the incoming IPC on the current thread
* back to a previously identity that was returned by {@link
@@ -522,18 +449,8 @@
* @see #clearCallingIdentity
*/
@CriticalNative
- @android.ravenwood.annotation.RavenwoodReplace
public static final native void restoreCallingIdentity(long token);
- /** @hide */
- public static final void restoreCallingIdentity$ravenwood(long token) {
- final SomeArgs args = Preconditions.requireNonNullViaRavenwoodRule(
- sIdentity$ravenwood).get();
- args.arg1 = ((token & (0x1 << 30)) != 0) ? Boolean.TRUE : Boolean.FALSE;
- args.argi1 = (int) (token >> 32);
- args.argi2 = (int) (token & ~(0x1 << 30));
- }
-
/**
* Convenience method for running the provided action enclosed in
* {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}.
@@ -708,16 +625,9 @@
*
* @hide
*/
- @android.ravenwood.annotation.RavenwoodReplace
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
public final native void markVintfStability();
- /** @hide */
- private void markVintfStability$ravenwood() {
- // This is not useful for Ravenwood which uses local binder.
- // TODO(b/361785059): Use real native libbinder.
- }
-
/**
* Use a VINTF-stability binder w/o VINTF requirements. Should be called
* on a binder before it is sent out of process.
@@ -736,14 +646,8 @@
* in order to prevent the process from holding on to objects longer than
* it needs to.
*/
- @android.ravenwood.annotation.RavenwoodReplace
public static final native void flushPendingCommands();
- /** @hide */
- public static final void flushPendingCommands$ravenwood() {
- // Ravenwood doesn't support IPC; ignored
- }
-
/**
* Add the calling thread to the IPC thread pool. This function does
* not return until the current process is exiting.
@@ -801,7 +705,6 @@
* <p>If you're creating a Binder token (a Binder object without an attached interface),
* you should use {@link #Binder(String)} instead.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public Binder() {
this(null);
}
@@ -818,12 +721,9 @@
* Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
* help identify them.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
- if (mObject != 0L) {
- NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
- }
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
@@ -842,7 +742,6 @@
* will be implemented for you to return the given owner IInterface when
* the corresponding descriptor is requested.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
@@ -851,7 +750,6 @@
/**
* Default implementation returns an empty interface name.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public @Nullable String getInterfaceDescriptor() {
return mDescriptor;
}
@@ -860,7 +758,6 @@
* Default implementation always returns true -- if you got here,
* the object is alive.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public boolean pingBinder() {
return true;
}
@@ -871,7 +768,6 @@
* Note that if you're calling on a local binder, this always returns true
* because your process is alive if you're calling it.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public boolean isBinderAlive() {
return true;
}
@@ -881,7 +777,6 @@
* to return the associated {@link IInterface} if it matches the requested
* descriptor.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
@@ -1080,7 +975,6 @@
*
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public @Nullable String getTransactionName(int transactionCode) {
return null;
}
@@ -1089,7 +983,6 @@
* @hide
*/
@VisibleForTesting
- @android.ravenwood.annotation.RavenwoodKeep
public final @Nullable String getTransactionTraceName(int transactionCode) {
final boolean isInterfaceUserDefined = getMaxTransactionId() == 0;
if (mTransactionTraceNames == null) {
@@ -1127,7 +1020,6 @@
return transactionTraceName;
}
- @android.ravenwood.annotation.RavenwoodKeep
private @NonNull String getSimpleDescriptor() {
String descriptor = mDescriptor;
if (descriptor == null) {
@@ -1147,7 +1039,6 @@
* @return The highest user-defined transaction id of all transactions.
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public int getMaxTransactionId() {
return 0;
}
@@ -1359,14 +1250,12 @@
/**
* Local implementation is a no-op.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
}
/**
* Local implementation is a no-op.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
return true;
}
@@ -1394,13 +1283,8 @@
}
}
- @android.ravenwood.annotation.RavenwoodReplace
private static native long getNativeBBinderHolder();
- private static long getNativeBBinderHolder$ravenwood() {
- return 0L;
- }
-
/**
* By default, we use the calling UID since we can always trust it.
*/
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 1100731..c22f46c 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -646,6 +646,37 @@
private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
/**
+ * This list is to hold strong reference to the frozen state callbacks. The callbacks are only
+ * weakly referenced by JNI so the strong references here are needed to keep the callbacks
+ * around until the proxy is GC'ed.
+ */
+ private List<IFrozenStateChangeCallback> mFrozenStateChangeCallbacks =
+ Collections.synchronizedList(new ArrayList<>());
+
+ /**
+ * See {@link IBinder#addFrozenStateChangeCallback(IFrozenStateChangeCallback)}
+ */
+ public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ addFrozenStateChangeCallbackNative(callback);
+ mFrozenStateChangeCallbacks.add(callback);
+ }
+
+ /**
+ * See {@link IBinder#removeFrozenStateChangeCallback}
+ */
+ public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+ mFrozenStateChangeCallbacks.remove(callback);
+ return removeFrozenStateChangeCallbackNative(callback);
+ }
+
+ private native void addFrozenStateChangeCallbackNative(IFrozenStateChangeCallback callback)
+ throws RemoteException;
+
+ private native boolean removeFrozenStateChangeCallbackNative(
+ IFrozenStateChangeCallback callback);
+
+ /**
* Perform a dump on the remote object
*
* @param fd The raw file descriptor that the dump is being sent to.
@@ -730,6 +761,17 @@
}
}
+ private static void invokeFrozenStateChangeCallback(
+ IFrozenStateChangeCallback callback, IBinder binderProxy, int stateIndex) {
+ try {
+ callback.onFrozenStateChanged(binderProxy,
+ IFrozenStateChangeCallback.State.values()[stateIndex]);
+ } catch (RuntimeException exc) {
+ Log.w("BinderNative", "Uncaught exception from frozen state change callback",
+ exc);
+ }
+ }
+
/**
* C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
* native IBinder object, and a DeathRecipientList.
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 30d2dec..a8267d1 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -17,6 +17,7 @@
package android.os;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -28,6 +29,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.sdk.Flags;
import android.sysprop.DeviceProperties;
import android.sysprop.SocProperties;
import android.sysprop.TelephonyProperties;
@@ -399,12 +401,35 @@
* device. This value never changes while a device is booted, but it may
* increase when the hardware manufacturer provides an OTA update.
* <p>
+ * Together with {@link SDK_MINOR_INT}, this constant defines the
+ * <pre>major.minor</pre> version of Android. <pre>SDK_INT</pre> is
+ * increased and <pre>SDK_MINOR_INT</pre> is set to 0 on new Android
+ * dessert releases. Between these, Android may also release so called
+ * minor releases where <pre>SDK_INT</pre> remains unchanged and
+ * <pre>SDK_MINOR_INT</pre> is increased. Minor releases can add new
+ * APIs, and have stricter guarantees around backwards compatibility
+ * (e.g. no changes gated by <pre>targetSdkVersion</pre>) compared to
+ * major releases.
+ * <p>
* Possible values are defined in {@link Build.VERSION_CODES}.
*/
public static final int SDK_INT = SystemProperties.getInt(
"ro.build.version.sdk", 0);
/**
+ * The minor SDK version of the software currently running on this hardware
+ * device. This value never changes while a device is booted, but it may
+ * increase when the hardware manufacturer provides an OTA update.
+ * <p>
+ * Together with {@link SDK_INT}, this constant defines the
+ * <pre>major.minor</pre> version of Android. See {@link SDK_INT} for
+ * more information.
+ */
+ @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
+ public static final int SDK_MINOR_INT = SystemProperties.getInt(
+ "ro.build.version.sdk_minor", 0);
+
+ /**
* The SDK version of the software that <em>initially</em> shipped on
* this hardware device. It <em>never</em> changes during the lifetime
* of the device, even when {@link #SDK_INT} increases due to an OTA
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index da2eec9..b2d9260 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -19,9 +19,9 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -51,9 +51,8 @@
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
[email protected]
[email protected](
- "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "ConcurrentMessageQueue";
private static final boolean DEBUG = false;
@@ -345,11 +344,17 @@
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private final AtomicInteger mNextBarrierToken = new AtomicInteger(1);
+ @RavenwoodRedirect
private static native long nativeInit();
+ @RavenwoodRedirect
private static native void nativeDestroy(long ptr);
+ @RavenwoodRedirect
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+ @RavenwoodRedirect
private static native void nativeWake(long ptr);
+ @RavenwoodRedirect
private static native boolean nativeIsPolling(long ptr);
+ @RavenwoodRedirect
private static native void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index bd81fb9..80f39bf 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -798,6 +798,12 @@
/**
* Remove any pending posts of messages with code 'what' that are in the
* message queue.
+ *
+ * Note that `Message#what` is 0 unless otherwise set.
+ * When calling `postMessage(Runnable)` or `postAtTime(Runnable, long)`,
+ * the `Runnable` is internally wrapped with a `Message` whose `what` is 0.
+ * Calling `removeMessages(0)` will remove all messages without a `what`,
+ * including posted `Runnable`s.
*/
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 50242ba..8185e8e 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -376,4 +376,53 @@
* return value instead.
*/
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
+
+ /** @hide */
+ interface IFrozenStateChangeCallback {
+ enum State {FROZEN, UNFROZEN};
+
+ /**
+ * Interface for receiving a callback when the process hosting an IBinder
+ * has changed its frozen state.
+ * @param who The IBinder whose hosting process has changed state.
+ * @param state The latest state.
+ */
+ void onFrozenStateChanged(@NonNull IBinder who, State state);
+ }
+
+ /**
+ * {@link addFrozenStateChangeCallback} provides a callback mechanism to notify about process
+ * frozen/unfrozen events. Upon registration and any subsequent state changes, the callback is
+ * invoked with the latest process frozen state.
+ *
+ * <p>If the listener process (the one using this API) is itself frozen, state change events
+ * might be combined into a single one with the latest frozen state. This single event would
+ * then be delivered when the listener process becomes unfrozen. Similarly, if an event happens
+ * before the previous event is consumed, they might be combined. This means the callback might
+ * not be called for every single state change, so don't rely on this API to count how many
+ * times the state has changed.</p>
+ *
+ * <p>The callback is automatically removed when all references to the binder proxy are
+ * dropped.</p>
+ *
+ * <p>You will only receive state change notifications for remote binders, as local binders by
+ * definition can't be frozen without you being frozen too.</p>
+ *
+ * <p>@throws {@link UnsupportedOperationException} if the kernel binder driver does not support
+ * this feature.
+ * @hide
+ */
+ default void addFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Unregister a {@link IFrozenStateChangeCallback}. The callback will no longer be invoked when
+ * the hosting process changes its frozen state.
+ * @hide
+ */
+ default boolean removeFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index 6b9b349..4474e7e 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -20,9 +20,9 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Handler;
-import android.os.Process;
-import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -42,9 +42,8 @@
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
[email protected]
[email protected](
- "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "MessageQueue";
private static final boolean DEBUG = false;
@@ -79,12 +78,18 @@
@UnsupportedAppUsage
private int mNextBarrierToken;
+ @RavenwoodRedirect
private native static long nativeInit();
+ @RavenwoodRedirect
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
+ @RavenwoodRedirect
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+ @RavenwoodRedirect
private native static void nativeWake(long ptr);
+ @RavenwoodRedirect
private native static boolean nativeIsPolling(long ptr);
+ @RavenwoodRedirect
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
diff --git a/core/java/android/os/LockedMessageQueue/MessageQueue.java b/core/java/android/os/LockedMessageQueue/MessageQueue.java
index b24e14b..f1affce 100644
--- a/core/java/android/os/LockedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LockedMessageQueue/MessageQueue.java
@@ -20,8 +20,9 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Handler;
-import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -44,9 +45,8 @@
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
[email protected]
[email protected](
- "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "LockedMessageQueue";
private static final boolean DEBUG = false;
@@ -389,12 +389,18 @@
@UnsupportedAppUsage
private int mNextBarrierToken;
+ @RavenwoodRedirect
private native static long nativeInit();
+ @RavenwoodRedirect
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
+ @RavenwoodRedirect
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+ @RavenwoodRedirect
private native static void nativeWake(long ptr);
+ @RavenwoodRedirect
private native static boolean nativeIsPolling(long ptr);
+ @RavenwoodRedirect
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index a1db9be..702fdc2 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -41,6 +41,9 @@
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
+ *
+ * If not specified, this value is 0.
+ * Use values other than 0 to indicate custom message codes.
*/
public int what;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 47096db..f728552 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -27,8 +27,8 @@
import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.ravenwood.annotation.RavenwoodClassLoadHook;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
import android.ravenwood.annotation.RavenwoodReplace;
import android.ravenwood.annotation.RavenwoodThrow;
import android.text.TextUtils;
@@ -233,8 +233,7 @@
* {@link #readSparseArray(ClassLoader, Class)}.
*/
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.Parcel_host")
+@RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
public final class Parcel {
private static final boolean DEBUG_RECYCLE = false;
@@ -389,10 +388,8 @@
@CriticalNative
private static native void nativeMarkSensitive(long nativePtr);
@FastNative
- @RavenwoodThrow
private static native void nativeMarkForBinder(long nativePtr, IBinder binder);
@CriticalNative
- @RavenwoodThrow
private static native boolean nativeIsForRpc(long nativePtr);
@CriticalNative
private static native int nativeDataSize(long nativePtr);
@@ -424,14 +421,12 @@
private static native int nativeWriteFloat(long nativePtr, float val);
@CriticalNative
private static native int nativeWriteDouble(long nativePtr, double val);
- @RavenwoodThrow
private static native void nativeSignalExceptionForError(int error);
@FastNative
private static native void nativeWriteString8(long nativePtr, String val);
@FastNative
private static native void nativeWriteString16(long nativePtr, String val);
@FastNative
- @RavenwoodThrow
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
@FastNative
private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
@@ -452,7 +447,6 @@
@FastNative
private static native String nativeReadString16(long nativePtr);
@FastNative
- @RavenwoodThrow
private static native IBinder nativeReadStrongBinder(long nativePtr);
@FastNative
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
@@ -477,17 +471,13 @@
private static native boolean nativeHasBinders(long nativePtr);
private static native boolean nativeHasBindersInRange(
long nativePtr, int offset, int length);
- @RavenwoodThrow
private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
- @RavenwoodThrow
private static native void nativeEnforceInterface(long nativePtr, String interfaceName);
@CriticalNative
- @RavenwoodThrow
private static native boolean nativeReplaceCallingWorkSourceUid(
long nativePtr, int workSourceUid);
@CriticalNative
- @RavenwoodThrow
private static native int nativeReadCallingWorkSourceUid(long nativePtr);
/** Last time exception with a stack trace was written */
@@ -496,7 +486,6 @@
private static final int WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS = 1000;
@CriticalNative
- @RavenwoodThrow
private static native long nativeGetOpenAshmemSize(long nativePtr);
public final static Parcelable.Creator<String> STRING_CREATOR
@@ -660,12 +649,10 @@
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RavenwoodThrow
public static native long getGlobalAllocSize();
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RavenwoodThrow
public static native long getGlobalAllocCount();
/**
@@ -1257,6 +1244,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @RavenwoodThrow(blockedBy = android.text.Spanned.class)
public final void writeCharSequence(@Nullable CharSequence val) {
TextUtils.writeToParcel(val, this, 0);
}
@@ -2996,7 +2984,7 @@
* @see #writeNoException
* @see #readException
*/
- @RavenwoodReplace
+ @RavenwoodReplace(blockedBy = AppOpsManager.class)
public final void writeException(@NonNull Exception e) {
AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
@@ -3035,10 +3023,15 @@
}
}
- /** @hide */
- public final void writeException$ravenwood(@NonNull Exception e) {
- // Ravenwood doesn't support IPC, no transaction headers needed
- writeInt(getExceptionCode(e));
+ private void writeException$ravenwood(@NonNull Exception e) {
+ int code = getExceptionCode(e);
+ writeInt(code);
+ if (code == 0) {
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+ throw new RuntimeException(e);
+ }
writeString(e.getMessage());
writeInt(0);
}
@@ -3096,7 +3089,7 @@
* @see #writeException
* @see #readException
*/
- @RavenwoodReplace
+ @RavenwoodReplace(blockedBy = AppOpsManager.class)
public final void writeNoException() {
AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
@@ -3127,9 +3120,7 @@
}
}
- /** @hide */
- public final void writeNoException$ravenwood() {
- // Ravenwood doesn't support IPC, no transaction headers needed
+ private void writeNoException$ravenwood() {
writeInt(0);
}
diff --git a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
index 79f229a..80c24a9 100644
--- a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
@@ -19,8 +19,9 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.os.Handler;
-import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -37,8 +38,6 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.PriorityQueue;
-import java.util.PriorityQueue;
-import java.util.PriorityQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -50,9 +49,8 @@
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
[email protected]
[email protected](
- "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "SemiConcurrentMessageQueue";
private static final boolean DEBUG = false;
@@ -338,11 +336,17 @@
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private final AtomicInteger mNextBarrierToken = new AtomicInteger(1);
+ @RavenwoodRedirect
private static native long nativeInit();
+ @RavenwoodRedirect
private static native void nativeDestroy(long ptr);
+ @RavenwoodRedirect
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+ @RavenwoodRedirect
private static native void nativeWake(long ptr);
+ @RavenwoodRedirect
private static native boolean nativeIsPolling(long ptr);
+ @RavenwoodRedirect
private static native void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index a15b3bb..46ae9d8 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -381,9 +381,11 @@
private MemoryRegistration(int size) {
// Round up to the nearest page size
final int PAGE_SIZE = OsConstants._SC_PAGE_SIZE;
- final int remainder = size % PAGE_SIZE;
- if (remainder != 0) {
- size += PAGE_SIZE - remainder;
+ if (PAGE_SIZE > 0) {
+ final int remainder = size % PAGE_SIZE;
+ if (remainder != 0) {
+ size += PAGE_SIZE - remainder;
+ }
}
mSize = size;
mReferenceCount = 1;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 50b73a9..81dc46e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2605,10 +2605,15 @@
* (Java) thread-local policy value.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @android.ravenwood.annotation.RavenwoodReplace
private static void onBinderStrictModePolicyChange(@ThreadPolicyMask int newPolicy) {
setBlockGuardPolicy(newPolicy);
}
+ private static void onBinderStrictModePolicyChange$ravenwood(@ThreadPolicyMask int newPolicy) {
+ /* no-op */
+ }
+
/**
* A tracked, critical time span. (e.g. during an animation.)
*
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 0ed1ab6..4c9a02c 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -109,6 +109,7 @@
private static final String TAG = "SystemClock";
private static volatile IAlarmManager sIAlarmManager;
+ private static volatile ITimeDetectorService sITimeDetectorService;
/**
* Since {@code nanoTime()} is arbitrary, anchor our Ravenwood clocks against it.
@@ -188,6 +189,14 @@
return sIAlarmManager;
}
+ private static ITimeDetectorService getITimeDetectorService() {
+ if (sITimeDetectorService == null) {
+ sITimeDetectorService = ITimeDetectorService.Stub
+ .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
+ }
+ return sITimeDetectorService;
+ }
+
/**
* Returns milliseconds since boot, not counting time spent in deep sleep.
*
@@ -314,15 +323,6 @@
}
/**
- * @see #currentNetworkTimeMillis(ITimeDetectorService)
- * @hide
- */
- public static long currentNetworkTimeMillis() {
- return currentNetworkTimeMillis(ITimeDetectorService.Stub
- .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE)));
- }
-
- /**
* Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
* using a remote network source outside the device.
* <p>
@@ -346,29 +346,29 @@
* @throws DateTimeException when no network time can be provided.
* @hide
*/
- public static long currentNetworkTimeMillis(
- ITimeDetectorService timeDetectorService) {
- if (timeDetectorService != null) {
- UnixEpochTime time;
- try {
- time = timeDetectorService.latestNetworkTime();
- } catch (ParcelableException e) {
- e.maybeRethrow(DateTimeException.class);
- throw new RuntimeException(e);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- if (time == null) {
- // This is not expected.
- throw new DateTimeException("Network based time is not available.");
- }
- long currentMillis = elapsedRealtime();
- long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
- return time.getUnixEpochTimeMillis() + deltaMs;
- } else {
+ public static long currentNetworkTimeMillis() {
+ ITimeDetectorService timeDetectorService = getITimeDetectorService();
+ if (timeDetectorService == null) {
throw new RuntimeException(new DeadSystemException());
}
+
+ UnixEpochTime time;
+ try {
+ time = timeDetectorService.latestNetworkTime();
+ } catch (ParcelableException e) {
+ e.maybeRethrow(DateTimeException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (time == null) {
+ // This is not expected.
+ throw new DateTimeException("Network based time is not available.");
+ }
+
+ long currentMillis = elapsedRealtime();
+ long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
+ return time.getUnixEpochTimeMillis() + deltaMs;
}
/**
@@ -396,14 +396,9 @@
*/
public static @NonNull Clock currentNetworkTimeClock() {
return new SimpleClock(ZoneOffset.UTC) {
- private ITimeDetectorService mSvc;
@Override
public long millis() {
- if (mSvc == null) {
- mSvc = ITimeDetectorService.Stub
- .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
- }
- return SystemClock.currentNetworkTimeMillis(mSvc);
+ return SystemClock.currentNetworkTimeMillis();
}
};
}
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 0a38691..e53873b 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -21,11 +21,13 @@
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.MutableInt;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -56,8 +58,7 @@
*/
@SystemApi
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.SystemProperties_host")
+@RavenwoodRedirectionClass("SystemProperties_host")
public class SystemProperties {
private static final String TAG = "SystemProperties";
private static final boolean TRACK_KEY_ACCESS = false;
@@ -75,7 +76,7 @@
@UnsupportedAppUsage
@GuardedBy("sChangeCallbacks")
- private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
+ static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
@GuardedBy("sRoReads")
private static final HashMap<String, MutableInt> sRoReads =
@@ -102,30 +103,18 @@
}
/** @hide */
+ @RavenwoodRedirect
public static void init$ravenwood(Map<String, String> values,
Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate) {
- native_init$ravenwood(values, keyReadablePredicate, keyWritablePredicate,
- SystemProperties::callChangeCallbacks);
- synchronized (sChangeCallbacks) {
- sChangeCallbacks.clear();
- }
+ throw RavenwoodEnvironment.notSupportedOnDevice();
}
/** @hide */
+ @RavenwoodRedirect
public static void reset$ravenwood() {
- native_reset$ravenwood();
- synchronized (sChangeCallbacks) {
- sChangeCallbacks.clear();
- }
+ throw RavenwoodEnvironment.notSupportedOnDevice();
}
- // These native methods are currently only implemented by Ravenwood, as it's the only
- // mechanism we have to jump to our RavenwoodNativeSubstitutionClass
- private static native void native_init$ravenwood(Map<String, String> values,
- Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate,
- Runnable changeCallback);
- private static native void native_reset$ravenwood();
-
// The one-argument version of native_get used to be a regular native function. Nowadays,
// we use the two-argument form of native_get all the time, but we can't just delete the
// one-argument overload: apps use it via reflection, as the UnsupportedAppUsage annotation
@@ -137,34 +126,46 @@
@FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native String native_get(String key, String def);
@FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native int native_get_int(String key, int def);
@FastNative
@UnsupportedAppUsage
+ @RavenwoodRedirect
private static native long native_get_long(String key, long def);
@FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native boolean native_get_boolean(String key, boolean def);
@FastNative
+ @RavenwoodRedirect
private static native long native_find(String name);
@FastNative
+ @RavenwoodRedirect
private static native String native_get(long handle);
@CriticalNative
+ @RavenwoodRedirect
private static native int native_get_int(long handle, int def);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_get_long(long handle, long def);
@CriticalNative
+ @RavenwoodRedirect
private static native boolean native_get_boolean(long handle, boolean def);
// _NOT_ FastNative: native_set performs IPC and can block
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native void native_set(String key, String def);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native void native_add_change_callback();
+ @RavenwoodRedirect
private static native void native_report_sysprop_change();
/**
@@ -300,7 +301,7 @@
}
@SuppressWarnings("unused") // Called from native code.
- private static void callChangeCallbacks() {
+ static void callChangeCallbacks() {
ArrayList<Runnable> callbacks = null;
synchronized (sChangeCallbacks) {
//Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
diff --git a/core/java/android/os/TransactionTooLargeException.java b/core/java/android/os/TransactionTooLargeException.java
index 79892e0..6b7cb33 100644
--- a/core/java/android/os/TransactionTooLargeException.java
+++ b/core/java/android/os/TransactionTooLargeException.java
@@ -47,7 +47,7 @@
* If possible, try to break up big requests into smaller pieces.
* </p><p>
* If you are implementing a service, it may help to impose size or complexity
- * contraints on the queries that clients can perform. For example, if the result set
+ * constraints on the queries that clients can perform. For example, if the result set
* could become large, then don't allow the client to request more than a few records
* at a time. Alternately, instead of returning all of the available data all at once,
* return the essential information first and make the client ask for additional information
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index f02d4a9..64a2dbc 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -540,6 +540,17 @@
/** @hide */
public abstract void validate();
+
+ /**
+ * If supported, truncate the length of this vibration effect to the provided length and return
+ * the result. Will always return null for repeating effects.
+ *
+ * @return The desired effect, or {@code null} if truncation is not applicable.
+ * @hide
+ */
+ @Nullable
+ public abstract VibrationEffect cropToLengthOrNull(int length);
+
/**
* Gets the estimated duration of the vibration in milliseconds.
*
@@ -866,6 +877,30 @@
}
}
+ /** @hide */
+ @Override
+ @Nullable
+ public VibrationEffect cropToLengthOrNull(int length) {
+ // drop repeating effects
+ if (mRepeatIndex >= 0) {
+ return null;
+ }
+
+ int segmentCount = mSegments.size();
+ if (segmentCount <= length) {
+ return this;
+ }
+
+ ArrayList truncated = new ArrayList(mSegments.subList(0, length));
+ Composed updated = new Composed(truncated, mRepeatIndex);
+ try {
+ updated.validate();
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ return updated;
+ }
+
@Override
public long getDuration() {
if (mRepeatIndex >= 0) {
@@ -1150,6 +1185,13 @@
"Vendor effect bundle must be non-empty");
}
+ /** @hide */
+ @Override
+ @Nullable
+ public VibrationEffect cropToLengthOrNull(int length) {
+ return null;
+ }
+
@Override
public long getDuration() {
return -1; // UNKNOWN
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 36233b7..84325b7 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +31,7 @@
import android.content.res.Resources;
import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
+import android.os.vibrator.Flags;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibratorFrequencyProfile;
import android.util.Log;
@@ -313,6 +315,86 @@
}
/**
+ * Checks whether the vibrator supports the creation of envelope effects.
+ *
+ * Envelope effects are defined by a series of frequency-amplitude pairs with specified
+ * transition times, allowing the creation of more complex vibration patterns.
+ *
+ * @return True if the hardware supports creating envelope effects, false otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public boolean areEnvelopeEffectsSupported() {
+ return getInfo().areEnvelopeEffectsSupported();
+ }
+
+ /**
+ * Retrieves the maximum duration supported for an envelope effect, in milliseconds.
+ *
+ * <p>If the device supports envelope effects (check {@link #areEnvelopeEffectsSupported}),
+ * this value will be positive. Devices with envelope effects capabilities guarantees a
+ * maximum duration equivalent to the product of {@link #getMaxEnvelopeEffectSize()} and
+ * {@link #getMaxEnvelopeEffectControlPointDurationMillis()}. If the device does not support
+ * envelope effects, this method will return 0.
+ *
+ * @return The maximum duration (in milliseconds) allowed for an envelope effect, or 0 if
+ * envelope effects are not supported.
+ */
+ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public int getMaxEnvelopeEffectDurationMillis() {
+ return getInfo().getMaxEnvelopeEffectDurationMillis();
+ }
+
+ /**
+ * Retrieves the maximum number of control points supported for an envelope effect.
+ *
+ * <p>If the device supports envelope effects (check {@link #areEnvelopeEffectsSupported}),
+ * this value will be positive. Devices with envelope effects capabilities guarantee support
+ * for a minimum of 16 control points. If the device does not support envelope effects,
+ * this method will return 0.
+ *
+ * @return the maximum number of control points allowed for an envelope effect, or 0 if
+ * envelope effects are not supported.
+ */
+ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public int getMaxEnvelopeEffectSize() {
+ return getInfo().getMaxEnvelopeEffectSize();
+ }
+
+ /**
+ * Retrieves the minimum duration supported between two control points within an envelope
+ * effect, in milliseconds.
+ *
+ * <p>If the device supports envelope effects (check {@link #areEnvelopeEffectsSupported}),
+ * this value will be positive. Devices with envelope effects capabilities guarantee
+ * support for durations down to at least 20 milliseconds. If the device does
+ * not support envelope effects, this method will return 0.
+ *
+ * @return the minimum allowed duration between two control points in an envelope effect,
+ * or 0 if envelope effects are not supported.
+ */
+ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public int getMinEnvelopeEffectControlPointDurationMillis() {
+ return getInfo().getMinEnvelopeEffectControlPointDurationMillis();
+ }
+
+ /**
+ * Retrieves the maximum duration supported between two control points within an envelope
+ * effect, in milliseconds.
+ *
+ * <p>If the device supports envelope effects (check {@link #areEnvelopeEffectsSupported}),
+ * this value will be positive. Devices with envelope effects capabilities guarantee support
+ * for durations up to at least 1 second. If the device does not support envelope effects,
+ * this method will return 0.
+ *
+ * @return the maximum allowed duration between two control points in an envelope effect,
+ * or 0 if envelope effects are not supported.
+ */
+ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public int getMaxEnvelopeEffectControlPointDurationMillis() {
+ return getInfo().getMaxEnvelopeEffectControlPointDurationMillis();
+ }
+
+ /**
* Configure an always-on haptics effect.
*
* @param alwaysOnId The board-specific always-on ID to configure.
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 4f8c24d..5378295e 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -60,6 +60,9 @@
private final int mPwleSizeMax;
private final float mQFactor;
private final FrequencyProfile mFrequencyProfile;
+ private final int mMaxEnvelopeEffectSize;
+ private final int mMinEnvelopeEffectControlPointDurationMillis;
+ private final int mMaxEnvelopeEffectControlPointDurationMillis;
VibratorInfo(Parcel in) {
mId = in.readInt();
@@ -73,6 +76,9 @@
mPwleSizeMax = in.readInt();
mQFactor = in.readFloat();
mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in);
+ mMaxEnvelopeEffectSize = in.readInt();
+ mMinEnvelopeEffectControlPointDurationMillis = in.readInt();
+ mMaxEnvelopeEffectControlPointDurationMillis = in.readInt();
}
public VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo) {
@@ -80,7 +86,10 @@
baseVibratorInfo.mSupportedBraking, baseVibratorInfo.mSupportedPrimitives,
baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax,
baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax,
- baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfile);
+ baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfile,
+ baseVibratorInfo.mMaxEnvelopeEffectSize,
+ baseVibratorInfo.mMinEnvelopeEffectControlPointDurationMillis,
+ baseVibratorInfo.mMaxEnvelopeEffectControlPointDurationMillis);
}
/**
@@ -111,7 +120,9 @@
@Nullable SparseBooleanArray supportedBraking,
@NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
- float qFactor, @NonNull FrequencyProfile frequencyProfile) {
+ float qFactor, @NonNull FrequencyProfile frequencyProfile,
+ int maxEnvelopeEffectSize, int minEnvelopeEffectControlPointDurationMillis,
+ int maxEnvelopeEffectControlPointDurationMillis) {
Preconditions.checkNotNull(supportedPrimitives);
Preconditions.checkNotNull(frequencyProfile);
mId = id;
@@ -125,6 +136,11 @@
mPwleSizeMax = pwleSizeMax;
mQFactor = qFactor;
mFrequencyProfile = frequencyProfile;
+ mMaxEnvelopeEffectSize = maxEnvelopeEffectSize;
+ mMinEnvelopeEffectControlPointDurationMillis =
+ minEnvelopeEffectControlPointDurationMillis;
+ mMaxEnvelopeEffectControlPointDurationMillis =
+ maxEnvelopeEffectControlPointDurationMillis;
}
@Override
@@ -140,6 +156,9 @@
dest.writeInt(mPwleSizeMax);
dest.writeFloat(mQFactor);
mFrequencyProfile.writeToParcel(dest, flags);
+ dest.writeInt(mMaxEnvelopeEffectSize);
+ dest.writeInt(mMinEnvelopeEffectControlPointDurationMillis);
+ dest.writeInt(mMaxEnvelopeEffectControlPointDurationMillis);
}
@Override
@@ -186,7 +205,12 @@
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
&& Objects.equals(mQFactor, that.mQFactor)
- && Objects.equals(mFrequencyProfile, that.mFrequencyProfile);
+ && Objects.equals(mFrequencyProfile, that.mFrequencyProfile)
+ && mMaxEnvelopeEffectSize == that.mMaxEnvelopeEffectSize
+ && mMinEnvelopeEffectControlPointDurationMillis
+ == that.mMinEnvelopeEffectControlPointDurationMillis
+ && mMaxEnvelopeEffectControlPointDurationMillis
+ == that.mMaxEnvelopeEffectControlPointDurationMillis;
}
@Override
@@ -215,6 +239,11 @@
+ ", mPwleSizeMax=" + mPwleSizeMax
+ ", mQFactor=" + mQFactor
+ ", mFrequencyProfile=" + mFrequencyProfile
+ + ", mMaxEnvelopeEffectSize=" + mMaxEnvelopeEffectSize
+ + ", mMinEnvelopeEffectControlPointDurationMillis="
+ + mMinEnvelopeEffectControlPointDurationMillis
+ + ", mMaxEnvelopeEffectControlPointDurationMillis="
+ + mMaxEnvelopeEffectControlPointDurationMillis
+ '}';
}
@@ -234,6 +263,11 @@
pw.println("pwleSizeMax = " + mPwleSizeMax);
pw.println("q-factor = " + mQFactor);
pw.println("frequencyProfile = " + mFrequencyProfile);
+ pw.println("mMaxEnvelopeEffectSize = " + mMaxEnvelopeEffectSize);
+ pw.println("mMinEnvelopeEffectControlPointDurationMillis = "
+ + mMinEnvelopeEffectControlPointDurationMillis);
+ pw.println("mMaxEnvelopeEffectControlPointDurationMillis = "
+ + mMaxEnvelopeEffectControlPointDurationMillis);
pw.decreaseIndent();
}
@@ -414,6 +448,58 @@
}
/**
+ * Check whether the vibrator supports the creation of envelope effects.
+ *
+ * <p>See {@link Vibrator#areEnvelopeEffectsSupported()} for more information on envelope
+ * effects.
+ *
+ * @return True if the hardware supports creating envelope effects, false otherwise.
+ */
+ public boolean areEnvelopeEffectsSupported() {
+ return hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
+ }
+
+ /**
+ * Calculates the maximum allowed duration for an envelope effect, measured in milliseconds.
+ *
+ * @return The maximum duration (in milliseconds) that an envelope effect can have.
+ */
+ public int getMaxEnvelopeEffectDurationMillis() {
+ return mMaxEnvelopeEffectSize * mMaxEnvelopeEffectControlPointDurationMillis;
+ }
+
+ /**
+ * Gets the maximum number of control points supported for envelope effects on this device.
+ *
+ * @return The maximum number of control points that can be used to define an envelope effect.
+ */
+ public int getMaxEnvelopeEffectSize() {
+ return mMaxEnvelopeEffectSize;
+ }
+
+ /**
+ * Gets the minimum allowed duration for any individual segment within an envelope effect,
+ * measured in milliseconds.
+ *
+ * @return The minimum duration (in milliseconds) that a segment within an envelope effect
+ * can have.
+ */
+ public int getMinEnvelopeEffectControlPointDurationMillis() {
+ return mMinEnvelopeEffectControlPointDurationMillis;
+ }
+
+ /**
+ * Gets the maximum allowed duration for any individual segment within an envelope effect,
+ * measured in milliseconds.
+ *
+ * @return The maximum duration (in milliseconds) that a segment within an envelope effect
+ * can have.
+ */
+ public int getMaxEnvelopeEffectControlPointDurationMillis() {
+ return mMaxEnvelopeEffectControlPointDurationMillis;
+ }
+
+ /**
* Check against this vibrator capabilities.
*
* @param capability one of IVibrator.CAP_*
@@ -489,6 +575,9 @@
if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
names.add("EXTERNAL_AMPLITUDE_CONTROL");
}
+ if (hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+ names.add("CAP_COMPOSE_PWLE_EFFECTS_V2");
+ }
return names.toArray(new String[names.size()]);
}
@@ -745,6 +834,9 @@
private float mQFactor = Float.NaN;
private FrequencyProfile mFrequencyProfile =
new FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
+ private int mMaxEnvelopeEffectSize;
+ private int mMinEnvelopeEffectControlPointDurationMillis;
+ private int mMaxEnvelopeEffectControlPointDurationMillis;
/** A builder class for a {@link VibratorInfo}. */
public Builder(int id) {
@@ -821,12 +913,46 @@
return this;
}
+ /**
+ * Configure the maximum number of control points supported for envelope effects on this
+ * device.
+ */
+ @NonNull
+ public Builder setMaxEnvelopeEffectSize(int maxEnvelopeEffectSize) {
+ mMaxEnvelopeEffectSize = maxEnvelopeEffectSize;
+ return this;
+ }
+
+ /**
+ * Configure the minimum supported duration for any individual segment within an
+ * envelope effect in milliseconds.
+ */
+ @NonNull
+ public Builder setMinEnvelopeEffectControlPointDurationMillis(
+ int minEnvelopeEffectControlPointDuration) {
+ mMinEnvelopeEffectControlPointDurationMillis = minEnvelopeEffectControlPointDuration;
+ return this;
+ }
+
+ /**
+ * Configure the maximum supported duration for any individual segment within an
+ * envelope effect in milliseconds.
+ */
+ @NonNull
+ public Builder setMaxEnvelopeEffectControlPointDurationMillis(
+ int maxEnvelopeEffectControlPointDuration) {
+ mMaxEnvelopeEffectControlPointDurationMillis = maxEnvelopeEffectControlPointDuration;
+ return this;
+ }
+
/** Build the configured {@link VibratorInfo}. */
@NonNull
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
- mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfile);
+ mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfile,
+ mMaxEnvelopeEffectSize, mMinEnvelopeEffectControlPointDurationMillis,
+ mMaxEnvelopeEffectControlPointDurationMillis);
}
/**
diff --git a/core/java/android/os/vibrator/MultiVibratorInfo.java b/core/java/android/os/vibrator/MultiVibratorInfo.java
index 5f32731..9c2b9782 100644
--- a/core/java/android/os/vibrator/MultiVibratorInfo.java
+++ b/core/java/android/os/vibrator/MultiVibratorInfo.java
@@ -59,7 +59,13 @@
integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
- mergedProfile);
+ mergedProfile,
+ integerLimitIntersection(vibrators,
+ VibratorInfo::getMaxEnvelopeEffectSize),
+ integerLimitIntersection(vibrators,
+ VibratorInfo::getMinEnvelopeEffectControlPointDurationMillis),
+ integerLimitIntersection(vibrators,
+ VibratorInfo::getMaxEnvelopeEffectControlPointDurationMillis));
}
private static int capabilitiesIntersection(VibratorInfo[] infos,
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index e6e5a27..bc6c570 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -30,13 +30,17 @@
import android.annotation.Nullable;
import android.content.res.Resources;
+import android.os.SystemProperties;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.Vibrator.VibrationIntensity;
import android.util.IndentingPrintWriter;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.function.Function;
/**
* List of device-specific internal vibration configuration loaded from platform config.xml.
@@ -50,6 +54,37 @@
public class VibrationConfig {
/**
+ * The default gain to be applied between vibration scale levels.
+ *
+ * <p>Scale levels are defined as the difference between the user vibration intensity setting
+ * and the device default config for each usage. The intensity values are defined as one of
+ * Vibrator.VIBRATION_INTENSITY_*.
+ *
+ * <p>A user setting HIGH set on a device with default value LOW will cause the vibration
+ * intensity to be scaled up 2 levels, i.e. scale with a factor of gain^2. A system with 3
+ * intensities LOW, MEDIUM and HIGH has the following 5 scale levels:
+ *
+ * <ol>
+ * <li>VERY_HIGH: user(HIGH) - device(LOW)
+ * <li>HIGH: user(HIGH) - device(MEDIUM) / user(MEDIUM) - device(LOW)
+ * <li>NONE: user == device
+ * <li>LOW: user(MEDIUM) - device(HIGH) / user(LOW) - device(MEDIUM)
+ * <li>VERY_LOW: user(LOW) - device(HIGH)
+ * </ol>
+ *
+ * <p>A device will only ever apply 3 out of these 5 levels based on the default intensity
+ * config set for each usage (e.g. config_default[Alarm|Ring|Notification]VibrationIntensity).
+ *
+ * <p>This value must be greater than 1. The {@link #DEFAULT_SCALE_LEVEL_GAIN} will be used if
+ * this property is undefined or invalid.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ static final String SCALE_LEVEL_GAIN_SYSTEM_PROPERTY =
+ "vendor.vibrator.scale.level.gain";
+
+ /**
* Hardcoded default scale level gain to be applied between each scale level to define their
* scale factor value.
*
@@ -69,7 +104,7 @@
private final int mRampDownDurationMs;
private final int mRequestVibrationParamsTimeoutMs;
private final int[] mRequestVibrationParamsForUsages;
-
+ private final float mDefaultVibrationScaleLevelGain;
private final boolean mIgnoreVibrationsOnWirelessCharger;
@VibrationIntensity
@@ -89,8 +124,18 @@
/** @hide */
public VibrationConfig(@Nullable Resources resources) {
- mDefaultVibrationAmplitude = resources.getInteger(
- com.android.internal.R.integer.config_defaultVibrationAmplitude);
+ this(resources, SystemProperties::get);
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public VibrationConfig(@Nullable Resources resources,
+ Function<String, String> systemPropertiesGetter) {
+ mDefaultVibrationAmplitude = loadInteger(resources,
+ com.android.internal.R.integer.config_defaultVibrationAmplitude,
+ DEFAULT_AMPLITUDE);
+ mDefaultVibrationScaleLevelGain = loadFloat(systemPropertiesGetter,
+ SCALE_LEVEL_GAIN_SYSTEM_PROPERTY, DEFAULT_SCALE_LEVEL_GAIN);
mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude);
mRampDownDurationMs = loadInteger(resources,
@@ -135,6 +180,15 @@
return res != null ? res.getFloat(resId) : 0f;
}
+ private static float loadFloat(Function<String, String> systemPropertiesGetter,
+ String propertyKey, float defaultValue) {
+ try {
+ return Float.parseFloat(systemPropertiesGetter.apply(propertyKey));
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) {
return res != null ? res.getInteger(resId) : defaultValue;
}
@@ -176,8 +230,10 @@
* for each level.
*/
public float getDefaultVibrationScaleLevelGain() {
- // TODO(b/356407380): add device config for this
- return DEFAULT_SCALE_LEVEL_GAIN;
+ if (mDefaultVibrationScaleLevelGain <= 1) {
+ return DEFAULT_SCALE_LEVEL_GAIN;
+ }
+ return mDefaultVibrationScaleLevelGain;
}
/**
@@ -270,6 +326,7 @@
return "VibrationConfig{"
+ "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger
+ ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude
+ + ", mDefaultVibrationScaleLevelGain=" + mDefaultVibrationScaleLevelGain
+ ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+ ", mRampStepDurationMs=" + mRampStepDurationMs
+ ", mRampDownDurationMs=" + mRampDownDurationMs
@@ -296,6 +353,7 @@
pw.increaseIndent();
pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger);
pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude);
+ pw.println("defaultVibrationScaleLevelGain = " + mDefaultVibrationScaleLevelGain);
pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude);
pw.println("rampStepDurationMs = " + mRampStepDurationMs);
pw.println("rampDownDurationMs = " + mRampDownDurationMs);
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 5174005..6c486db 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -241,3 +241,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "wallet_role_icon_property_enabled"
+ is_exported: true
+ namespace: "wallet_integration"
+ description: "This flag is used to enabled the Wallet Role s icon fetching from manifest property"
+ bug: "349942654"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 24f52d0..0a05f70 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1974,7 +1974,7 @@
/**
* Activity Action: Show Notification Policy access settings.
* <p>
- * Users can grant and deny access to Notification Policy (DND / Priority Modes) configuration
+ * Users can grant and deny access to Notification Policy (DND / Modes) configuration
* from here. Managed profiles cannot grant Notification Policy access.
* See {@link android.app.NotificationManager#isNotificationPolicyAccessGranted()} for more
* details.
@@ -5150,13 +5150,19 @@
public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
/**
- * The screen backlight brightness between 0 and 255.
+ * The screen backlight brightness between 1 (minimum) and 255 (maximum).
+ *
+ * Use {@link android.view.WindowManager.LayoutParams#screenBrightness} to set the screen
+ * brightness instead.
*/
@Readable
public static final String SCREEN_BRIGHTNESS = "screen_brightness";
/**
- * Control whether to enable automatic brightness mode.
+ * Controls whether to enable automatic brightness mode. Value can be set to
+ * {@link #SCREEN_BRIGHTNESS_MODE_MANUAL} or {@link #SCREEN_BRIGHTNESS_MODE_AUTOMATIC}.
+ * If {@link #SCREEN_BRIGHTNESS_MODE_AUTOMATIC} is set, the system may change
+ * {@link #SCREEN_BRIGHTNESS} automatically.
*/
@Readable
public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index 1f1a351..2b75493 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -2,6 +2,22 @@
container: "system"
flag {
+ name: "chooser_album_text"
+ is_exported: true
+ namespace: "intentresolver"
+ description: "Flag controlling the album text subtype hint for sharesheet"
+ bug: "323380224"
+}
+
+flag {
+ name: "enable_sharesheet_metadata_extra"
+ is_exported: true
+ namespace: "intentresolver"
+ description: "This flag enables sharesheet metadata to be displayed to users."
+ bug: "318942069"
+}
+
+flag {
name: "chooser_payload_toggling"
is_exported: true
namespace: "intentresolver"
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 918e591..3d8d933 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1044,23 +1044,19 @@
rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
} else if (MANUAL_TAG.equals(tag)) {
- ZenRule manualRule = readRuleXml(parser);
- if (manualRule != null) {
- rt.manualRule = manualRule;
-
- // Manual rule may be present prior to modes_ui if it were on, but in that
- // case it would not have a set policy, so make note of the need to set
- // it up later.
- readManualRule = true;
- if (rt.manualRule.zenPolicy == null) {
- readManualRuleWithoutPolicy = true;
- }
+ rt.manualRule = readRuleXml(parser);
+ // Manual rule may be present prior to modes_ui if it were on, but in that
+ // case it would not have a set policy, so make note of the need to set
+ // it up later.
+ readManualRule = true;
+ if (rt.manualRule.zenPolicy == null) {
+ readManualRuleWithoutPolicy = true;
}
} else if (AUTOMATIC_TAG.equals(tag)
|| (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
final String id = parser.getAttributeValue(null, RULE_ATT_ID);
- final ZenRule automaticRule = readRuleXml(parser);
- if (id != null && automaticRule != null) {
+ if (id != null) {
+ final ZenRule automaticRule = readRuleXml(parser);
automaticRule.id = id;
if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) {
String deletedRuleKey = deletedRuleKey(automaticRule);
@@ -1177,16 +1173,13 @@
out.endTag(null, ZEN_TAG);
}
+ @NonNull
public static ZenRule readRuleXml(TypedXmlPullParser parser) {
final ZenRule rt = new ZenRule();
rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
- rt.zenMode = tryParseZenMode(zen, -1);
- if (rt.zenMode == -1) {
- Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
- return null;
- }
+ rt.zenMode = tryParseZenMode(zen, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
@@ -2939,7 +2932,7 @@
}
}
- // TODO: b/333527800 - Rename to isActive()
+ // TODO: b/363193376 - Rename to isActive()
public boolean isAutomaticActive() {
if (Flags.modesApi() && Flags.modesUi()) {
if (!enabled || getPkg() == null) {
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 2669391..be0d7b3 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -393,6 +393,46 @@
}
/**
+ * Base Zen Policy used when {@link android.app.NotificationManager#setInterruptionFilter} is
+ * called with {@link android.app.NotificationManager#INTERRUPTION_FILTER_ALARMS} or an
+ * {@link android.app.AutomaticZenRule} specifies said filter.
+ *
+ * <p>Note that <em>visual effects</em> for filtered notifications are unset in this base
+ * policy, so should be merged on top of the default policy's visual effects (see
+ * {@link #overwrittenWith(ZenPolicy)}).
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static ZenPolicy getBasePolicyInterruptionFilterAlarms() {
+ return new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowPriorityChannels(false)
+ .build();
+ }
+
+ /**
+ * Base Zen Policy used when {@link android.app.NotificationManager#setInterruptionFilter} is
+ * called with {@link android.app.NotificationManager#INTERRUPTION_FILTER_NONE} or an
+ * {@link android.app.AutomaticZenRule} specifies said filter.
+ *
+ * <p>Note that <em>visual effects</em> for filtered notifications are unset in this base
+ * policy, so it should be merged on top of the device default policy's visual effects (see
+ * {@link #overwrittenWith(ZenPolicy)}).
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static ZenPolicy getBasePolicyInterruptionFilterNone() {
+ return new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowPriorityChannels(false)
+ .build();
+ }
+
+ /**
* Conversation type that can bypass DND.
* @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
* {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}.
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index 5d84d17..c2ad508 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -28,41 +28,6 @@
*/
public class ClientFlags {
/**
- * @see Flags#noBreakNoHyphenationSpan()
- */
- public static boolean noBreakNoHyphenationSpan() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN);
- }
-
- /**
- * @see Flags#phraseStrictFallback()
- */
- public static boolean phraseStrictFallback() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_PHRASE_STRICT_FALLBACK);
- }
-
- /**
- * @see Flags#useBoundsForWidth()
- */
- public static boolean useBoundsForWidth() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_USE_BOUNDS_FOR_WIDTH);
- }
-
- /**
- * @see Flags#fixLineHeightForLocale()
- */
- public static boolean fixLineHeightForLocale() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE);
- }
-
- /**
- * @see Flags#icuBidiMigration()
- */
- public static boolean icuBidiMigration() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_ICU_BIDI_MIGRATION);
- }
-
- /**
* @see Flags#fixMisalignedContextMenu()
*/
public static boolean fixMisalignedContextMenu() {
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 896e087..31a2263 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -42,8 +42,6 @@
import android.text.style.ReplacementSpan;
import android.util.Pools.SynchronizedPool;
-import com.android.text.flags.Flags;
-
import java.util.Arrays;
/**
@@ -201,14 +199,11 @@
* @hide
*/
public @Layout.Direction int getParagraphDir() {
- if (Flags.icuBidiMigration()) {
- if (mBidi == null) {
- return Layout.DIR_LEFT_TO_RIGHT;
- }
- return (mBidi.getParaLevel() & 0x01) == 0
- ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT;
+ if (mBidi == null) {
+ return Layout.DIR_LEFT_TO_RIGHT;
}
- return mParaDir;
+ return (mBidi.getParaLevel() & 0x01) == 0
+ ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT;
}
/**
@@ -219,71 +214,62 @@
*/
public Directions getDirections(@IntRange(from = 0) int start, // inclusive
@IntRange(from = 0) int end) { // exclusive
- if (Flags.icuBidiMigration()) {
- // Easy case: mBidi == null means the text is all LTR and no bidi suppot is needed.
- if (mBidi == null) {
- return Layout.DIRS_ALL_LEFT_TO_RIGHT;
- }
-
- // Easy case: If the original text only contains single directionality run, the
- // substring is only single run.
- if (start == end) {
- if ((mBidi.getParaLevel() & 0x01) == 0) {
- return Layout.DIRS_ALL_LEFT_TO_RIGHT;
- } else {
- return Layout.DIRS_ALL_RIGHT_TO_LEFT;
- }
- }
-
- // Okay, now we need to generate the line instance.
- Bidi bidi = mBidi.createLineBidi(start, end);
-
- // Easy case: If the line instance only contains single directionality run, no need
- // to reorder visually.
- if (bidi.getRunCount() == 1) {
- if (bidi.getRunLevel(0) == 1) {
- return Layout.DIRS_ALL_RIGHT_TO_LEFT;
- } else if (bidi.getRunLevel(0) == 0) {
- return Layout.DIRS_ALL_LEFT_TO_RIGHT;
- } else {
- return new Directions(new int[] {
- 0, bidi.getRunLevel(0) << Layout.RUN_LEVEL_SHIFT | (end - start)});
- }
- }
-
- // Reorder directionality run visually.
- byte[] levels = new byte[bidi.getRunCount()];
- for (int i = 0; i < bidi.getRunCount(); ++i) {
- levels[i] = (byte) bidi.getRunLevel(i);
- }
- int[] visualOrders = Bidi.reorderVisual(levels);
-
- int[] dirs = new int[bidi.getRunCount() * 2];
- for (int i = 0; i < bidi.getRunCount(); ++i) {
- int vIndex;
- if ((mBidi.getBaseLevel() & 0x01) == 1) {
- // For the historical reasons, if the base directionality is RTL, the Android
- // draws from the right, i.e. the visually reordered run needs to be reversed.
- vIndex = visualOrders[bidi.getRunCount() - i - 1];
- } else {
- vIndex = visualOrders[i];
- }
-
- // Special packing of dire
- dirs[i * 2] = bidi.getRunStart(vIndex);
- dirs[i * 2 + 1] = bidi.getRunLevel(vIndex) << Layout.RUN_LEVEL_SHIFT
- | (bidi.getRunLimit(vIndex) - dirs[i * 2]);
- }
-
- return new Directions(dirs);
- }
- if (mLtrWithoutBidi) {
+ // Easy case: mBidi == null means the text is all LTR and no bidi suppot is needed.
+ if (mBidi == null) {
return Layout.DIRS_ALL_LEFT_TO_RIGHT;
}
- final int length = end - start;
- return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
- length);
+ // Easy case: If the original text only contains single directionality run, the
+ // substring is only single run.
+ if (start == end) {
+ if ((mBidi.getParaLevel() & 0x01) == 0) {
+ return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ } else {
+ return Layout.DIRS_ALL_RIGHT_TO_LEFT;
+ }
+ }
+
+ // Okay, now we need to generate the line instance.
+ Bidi bidi = mBidi.createLineBidi(start, end);
+
+ // Easy case: If the line instance only contains single directionality run, no need
+ // to reorder visually.
+ if (bidi.getRunCount() == 1) {
+ if (bidi.getRunLevel(0) == 1) {
+ return Layout.DIRS_ALL_RIGHT_TO_LEFT;
+ } else if (bidi.getRunLevel(0) == 0) {
+ return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ } else {
+ return new Directions(new int[] {
+ 0, bidi.getRunLevel(0) << Layout.RUN_LEVEL_SHIFT | (end - start)});
+ }
+ }
+
+ // Reorder directionality run visually.
+ byte[] levels = new byte[bidi.getRunCount()];
+ for (int i = 0; i < bidi.getRunCount(); ++i) {
+ levels[i] = (byte) bidi.getRunLevel(i);
+ }
+ int[] visualOrders = Bidi.reorderVisual(levels);
+
+ int[] dirs = new int[bidi.getRunCount() * 2];
+ for (int i = 0; i < bidi.getRunCount(); ++i) {
+ int vIndex;
+ if ((mBidi.getBaseLevel() & 0x01) == 1) {
+ // For the historical reasons, if the base directionality is RTL, the Android
+ // draws from the right, i.e. the visually reordered run needs to be reversed.
+ vIndex = visualOrders[bidi.getRunCount() - i - 1];
+ } else {
+ vIndex = visualOrders[i];
+ }
+
+ // Special packing of dire
+ dirs[i * 2] = bidi.getRunStart(vIndex);
+ dirs[i * 2 + 1] = bidi.getRunLevel(vIndex) << Layout.RUN_LEVEL_SHIFT
+ | (bidi.getRunLimit(vIndex) - dirs[i * 2]);
+ }
+
+ return new Directions(dirs);
}
/**
@@ -681,84 +667,56 @@
}
}
- if (Flags.icuBidiMigration()) {
- if ((textDir == TextDirectionHeuristics.LTR
- || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
- || textDir == TextDirectionHeuristics.ANYRTL_LTR)
- && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
- mLevels.clear();
- mLtrWithoutBidi = true;
- return;
- }
- final int bidiRequest;
- if (textDir == TextDirectionHeuristics.LTR) {
- bidiRequest = Bidi.LTR;
- } else if (textDir == TextDirectionHeuristics.RTL) {
- bidiRequest = Bidi.RTL;
- } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
- bidiRequest = Bidi.LEVEL_DEFAULT_LTR;
- } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
- bidiRequest = Bidi.LEVEL_DEFAULT_RTL;
- } else {
- final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
- bidiRequest = isRtl ? Bidi.RTL : Bidi.LTR;
- }
- mBidi = new Bidi(mCopiedBuffer, 0, null, 0, mCopiedBuffer.length, bidiRequest);
-
- if (mCopiedBuffer.length > 0
- && mBidi.getParagraphIndex(mCopiedBuffer.length - 1) != 0) {
- // Historically, the MeasuredParagraph does not treat the CR letters as paragraph
- // breaker but ICU BiDi treats it as paragraph breaker. In the MeasureParagraph,
- // the given range always represents a single paragraph, so if the BiDi object has
- // multiple paragraph, it should contains a CR letters in the text. Using CR is not
- // common in Android and also it should not penalize the easy case, e.g. all LTR,
- // check the paragraph count here and replace the CR letters and re-calculate
- // BiDi again.
- for (int i = 0; i < mTextLength; ++i) {
- if (Character.isSurrogate(mCopiedBuffer[i])) {
- // All block separators are in BMP.
- continue;
- }
- if (UCharacter.getDirection(mCopiedBuffer[i])
- == UCharacterDirection.BLOCK_SEPARATOR) {
- mCopiedBuffer[i] = OBJECT_REPLACEMENT_CHARACTER;
- }
- }
- mBidi = new Bidi(mCopiedBuffer, 0, null, 0, mCopiedBuffer.length, bidiRequest);
- }
- mLevels.resize(mTextLength);
- byte[] rawArray = mLevels.getRawArray();
- for (int i = 0; i < mTextLength; ++i) {
- rawArray[i] = mBidi.getLevelAt(i);
- }
- mLtrWithoutBidi = false;
- return;
- }
if ((textDir == TextDirectionHeuristics.LTR
|| textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
|| textDir == TextDirectionHeuristics.ANYRTL_LTR)
&& TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
mLevels.clear();
- mParaDir = Layout.DIR_LEFT_TO_RIGHT;
mLtrWithoutBidi = true;
- } else {
- final int bidiRequest;
- if (textDir == TextDirectionHeuristics.LTR) {
- bidiRequest = Layout.DIR_REQUEST_LTR;
- } else if (textDir == TextDirectionHeuristics.RTL) {
- bidiRequest = Layout.DIR_REQUEST_RTL;
- } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
- bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
- } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
- bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
- } else {
- final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
- bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
- }
- mLevels.resize(mTextLength);
- mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
- mLtrWithoutBidi = false;
+ return;
}
+ final int bidiRequest;
+ if (textDir == TextDirectionHeuristics.LTR) {
+ bidiRequest = Bidi.LTR;
+ } else if (textDir == TextDirectionHeuristics.RTL) {
+ bidiRequest = Bidi.RTL;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
+ bidiRequest = Bidi.LEVEL_DEFAULT_LTR;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
+ bidiRequest = Bidi.LEVEL_DEFAULT_RTL;
+ } else {
+ final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
+ bidiRequest = isRtl ? Bidi.RTL : Bidi.LTR;
+ }
+ mBidi = new Bidi(mCopiedBuffer, 0, null, 0, mCopiedBuffer.length, bidiRequest);
+
+ if (mCopiedBuffer.length > 0
+ && mBidi.getParagraphIndex(mCopiedBuffer.length - 1) != 0) {
+ // Historically, the MeasuredParagraph does not treat the CR letters as paragraph
+ // breaker but ICU BiDi treats it as paragraph breaker. In the MeasureParagraph,
+ // the given range always represents a single paragraph, so if the BiDi object has
+ // multiple paragraph, it should contains a CR letters in the text. Using CR is not
+ // common in Android and also it should not penalize the easy case, e.g. all LTR,
+ // check the paragraph count here and replace the CR letters and re-calculate
+ // BiDi again.
+ for (int i = 0; i < mTextLength; ++i) {
+ if (Character.isSurrogate(mCopiedBuffer[i])) {
+ // All block separators are in BMP.
+ continue;
+ }
+ if (UCharacter.getDirection(mCopiedBuffer[i])
+ == UCharacterDirection.BLOCK_SEPARATOR) {
+ mCopiedBuffer[i] = OBJECT_REPLACEMENT_CHARACTER;
+ }
+ }
+ mBidi = new Bidi(mCopiedBuffer, 0, null, 0, mCopiedBuffer.length, bidiRequest);
+ }
+ mLevels.resize(mTextLength);
+ byte[] rawArray = mLevels.getRawArray();
+ for (int i = 0; i < mTextLength; ++i) {
+ rawArray[i] = mBidi.getLevelAt(i);
+ }
+ mLtrWithoutBidi = false;
}
private void applyReplacementRun(@NonNull ReplacementSpan replacement,
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 9e02460..076721f 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -55,11 +55,6 @@
* List of text flags to be transferred to the application process.
*/
public static final String[] TEXT_ACONFIGS_FLAGS = {
- Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN,
- Flags.FLAG_PHRASE_STRICT_FALLBACK,
- Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
- Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE,
- Flags.FLAG_ICU_BIDI_MIGRATION,
Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU,
};
@@ -69,11 +64,6 @@
* The order must be the same to the TEXT_ACONFIG_FLAGS.
*/
public static final boolean[] TEXT_ACONFIG_DEFAULT_VALUE = {
- Flags.noBreakNoHyphenationSpan(),
- Flags.phraseStrictFallback(),
- Flags.useBoundsForWidth(),
- Flags.fixLineHeightForLocale(),
- Flags.icuBidiMigration(),
Flags.fixMisalignedContextMenu(),
};
diff --git a/core/java/android/text/flags/24Q3.aconfig b/core/java/android/text/flags/24Q3.aconfig
new file mode 100644
index 0000000..7035fc8
--- /dev/null
+++ b/core/java/android/text/flags/24Q3.aconfig
@@ -0,0 +1,54 @@
+package: "com.android.text.flags"
+container: "system"
+
+# This aconfig file contains released flags in 24Q3 those cannot be removed.
+
+flag {
+ name: "use_bounds_for_width"
+ is_exported: true
+ namespace: "text"
+ description: "Feature flag for preventing horizontal clipping."
+ bug: "63938206"
+}
+
+flag {
+ name: "word_style_auto"
+ is_exported: true
+ namespace: "text"
+ description: "A feature flag that implements line break word style auto."
+ bug: "280005585"
+}
+
+flag {
+ name: "letter_spacing_justification"
+ is_exported: true
+ namespace: "text"
+ description: "A feature flag that implement inter character justification."
+ bug: "283193133"
+}
+
+flag {
+ name: "fix_line_height_for_locale"
+ is_exported: true
+ namespace: "text"
+ description: "Feature flag that preserve the line height of the TextView and EditText even if the the locale is different from Latin"
+ bug: "303326708"
+}
+
+flag {
+ name: "new_fonts_fallback_xml"
+ is_exported: true
+ namespace: "text"
+ description: "Feature flag for deprecating fonts.xml. By setting true for this feature flag, the new font configuration XML, /system/etc/font_fallback.xml is used. The new XML has a new syntax and flexibility of variable font declarations, but it is not compatible with the apps that reads fonts.xml. So, fonts.xml is maintained as a subset of the font_fallback.xml"
+ # Make read only, as it could be used before the Settings provider is initialized.
+ is_fixed_read_only: true
+ bug: "281769620"
+}
+
+flag {
+ name: "no_break_no_hyphenation_span"
+ is_exported: true
+ namespace: "text"
+ description: "A feature flag that adding new spans that prevents line breaking and hyphenation."
+ bug: "283193586"
+}
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index d33c95e..3c61f4f 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -2,47 +2,6 @@
container: "system"
flag {
- name: "vendor_custom_locale_fallback"
- namespace: "text"
- description: "A feature flag that adds custom locale fallback to the vendor customization XML. This enables vendors to add their locale specific fonts, e.g. Japanese font."
- is_fixed_read_only: true
- bug: "278768958"
-}
-
-flag {
- name: "new_fonts_fallback_xml"
- is_exported: true
- namespace: "text"
- description: "Feature flag for deprecating fonts.xml. By setting true for this feature flag, the new font configuration XML, /system/etc/font_fallback.xml is used. The new XML has a new syntax and flexibility of variable font declarations, but it is not compatible with the apps that reads fonts.xml. So, fonts.xml is maintained as a subset of the font_fallback.xml"
- # Make read only, as it could be used before the Settings provider is initialized.
- is_fixed_read_only: true
- bug: "281769620"
-}
-
-flag {
- name: "fix_double_underline"
- namespace: "text"
- description: "Feature flag for fixing double underline because of the multiple font used in the single line."
- bug: "297336724"
-}
-
-flag {
- name: "fix_line_height_for_locale"
- is_exported: true
- namespace: "text"
- description: "Feature flag that preserve the line height of the TextView and EditText even if the the locale is different from Latin"
- bug: "303326708"
-}
-
-flag {
- name: "no_break_no_hyphenation_span"
- is_exported: true
- namespace: "text"
- description: "A feature flag that adding new spans that prevents line breaking and hyphenation."
- bug: "283193586"
-}
-
-flag {
name: "use_optimized_boottime_font_loading"
namespace: "text"
description: "Feature flag ensuring that font is loaded once and asynchronously."
@@ -66,44 +25,6 @@
}
flag {
- name: "phrase_strict_fallback"
- namespace: "text"
- description: "Feature flag for automatic fallback from phrase based line break to strict line break."
- bug: "281970875"
-}
-
-flag {
- name: "use_bounds_for_width"
- is_exported: true
- namespace: "text"
- description: "Feature flag for preventing horizontal clipping."
- bug: "63938206"
-}
-
-flag {
- name: "deprecate_ui_fonts"
- namespace: "text"
- description: "Feature flag for deprecating UI fonts. By setting true for this feature flag, the elegant text height of will be turned on by default unless explicitly setting it to false by attribute or Java API call."
- bug: "279646685"
-}
-
-flag {
- name: "word_style_auto"
- is_exported: true
- namespace: "text"
- description: "A feature flag that implements line break word style auto."
- bug: "280005585"
-}
-
-flag {
- name: "letter_spacing_justification"
- is_exported: true
- namespace: "text"
- description: "A feature flag that implement inter character justification."
- bug: "283193133"
-}
-
-flag {
name: "escape_clears_focus"
namespace: "text"
description: "Feature flag for clearing focus when the escape key is pressed."
@@ -142,22 +63,6 @@
}
flag {
- name: "icu_bidi_migration"
- namespace: "text"
- description: "A flag for replacing AndroidBidi with android.icu.text.Bidi."
- bug: "317144801"
-}
-
-flag {
- name: "lazy_variation_instance"
- namespace: "text"
- description: "A flag for enabling lazy variation instance creation."
- # Make read only, as it could be used before the Settings provider is initialized.
- is_fixed_read_only: true
- bug: "324676775"
-}
-
-flag {
name: "handwriting_end_of_line_tap"
namespace: "text"
description: "Initiate handwriting when stylus taps at the end of a line in a focused non-empty TextView with the cursor at the end of that line"
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/util/EventLog.java b/core/java/android/util/EventLog.java
index 0a73fd1..00545da 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -21,6 +21,10 @@
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodThrow;
import java.io.BufferedReader;
import java.io.FileReader;
@@ -48,9 +52,8 @@
* They carry a payload of one or more int, long, or String values. The
* event-log-tags file defines the payload contents for each type code.
*/
[email protected]
[email protected](
- "com.android.platform.test.ravenwood.nativesubstitution.EventLog_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("EventLog_host")
public class EventLog {
/** @hide */ public EventLog() {}
@@ -339,6 +342,7 @@
* @param value A value to log
* @return The number of bytes written
*/
+ @RavenwoodRedirect
public static native int writeEvent(int tag, int value);
/**
@@ -347,6 +351,7 @@
* @param value A value to log
* @return The number of bytes written
*/
+ @RavenwoodRedirect
public static native int writeEvent(int tag, long value);
/**
@@ -355,6 +360,7 @@
* @param value A value to log
* @return The number of bytes written
*/
+ @RavenwoodRedirect
public static native int writeEvent(int tag, float value);
/**
@@ -363,6 +369,7 @@
* @param str A value to log
* @return The number of bytes written
*/
+ @RavenwoodRedirect
public static native int writeEvent(int tag, String str);
/**
@@ -371,6 +378,7 @@
* @param list A list of values to log
* @return The number of bytes written
*/
+ @RavenwoodRedirect
public static native int writeEvent(int tag, Object... list);
/**
@@ -379,6 +387,7 @@
* @param output container to add events into
* @throws IOException if something goes wrong reading events
*/
+ @RavenwoodThrow
public static native void readEvents(int[] tags, Collection<Event> output)
throws IOException;
@@ -391,6 +400,7 @@
* @hide
*/
@SystemApi
+ @RavenwoodThrow
public static native void readEventsOnWrapping(int[] tags, long timestamp,
Collection<Event> output)
throws IOException;
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index fe536a6..22583ac 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -19,6 +19,7 @@
import android.annotation.HalfFloat;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import libcore.util.FP16;
@@ -92,6 +93,7 @@
* <p>This table shows that numbers higher than 1024 lose all fractional precision.</p>
*/
@SuppressWarnings("SimplifiableIfStatement")
+@RavenwoodKeepWholeClass
public final class Half extends Number implements Comparable<Half> {
/**
* The number of bits used to represent a half-precision float value.
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index feb80cc..ca5798a 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -112,6 +112,11 @@
}
}
+ // @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ public synchronized void clear() {
+ mLog.clear();
+ }
+
public static class ReadOnlyLocalLog {
private final LocalLog mLog;
ReadOnlyLocalLog(LocalLog log) {
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 9ecb4cb..b2017a5 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -224,7 +224,6 @@
/**
* Similar to {@link #wtf(String, String)}, but does not output anything to the log.
*/
- @android.ravenwood.annotation.RavenwoodThrow
public static void wtfQuiet(@Nullable String tag, @NonNull String msg) {
Log.wtfQuiet(Log.LOG_ID_SYSTEM, tag, msg, true);
}
@@ -243,7 +242,6 @@
* @see Log#wtfStack(String, String)
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- @android.ravenwood.annotation.RavenwoodThrow
public static int wtfStack(@Nullable String tag, @NonNull String msg) {
return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, true, true);
}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
deleted file mode 100644
index 55ad4ae..0000000
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.app.ActivityManager;
-import android.graphics.GraphicBuffer;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.window.PictureInPictureSurfaceTransaction;
-import android.window.TaskSnapshot;
-import android.window.WindowAnimationState;
-
-import com.android.internal.os.IResultReceiver;
-
-/**
- * Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the
- * runner control certain aspects of the recents animation, and to notify window manager when the
- * animation has completed.
- *
- * {@hide}
- */
-interface IRecentsAnimationController {
-
- /**
- * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the
- * current set of task ids provided to the handler.
- */
- TaskSnapshot screenshotTask(int taskId);
-
- /**
- * Sets the final surface transaction on a Task. This is used by Launcher to notify the system
- * that animating Activity to PiP has completed and the associated task surface should be
- * updated accordingly. This should be called before `finish`
- * @param taskId for which the leash should be updated
- * @param finishTransaction leash operations for the final transform.
- * @param overlay the surface control for an overlay being shown above the pip (can be null)
- */
- void setFinishTaskTransaction(int taskId,
- in PictureInPictureSurfaceTransaction finishTransaction, in SurfaceControl overlay);
-
- /**
- * Notifies to the system that the animation into Recents should end, and all leashes associated
- * with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then
- * the home activity should be moved to the top. Otherwise, the home activity is hidden and the
- * user is returned to the app.
- * @param sendUserLeaveHint If set to true, {@link Activity#onUserLeaving} will be sent to the
- * top resumed app, false otherwise.
- */
- @UnsupportedAppUsage
- void finish(boolean moveHomeToTop, boolean sendUserLeaveHint, in IResultReceiver finishCb);
-
- /**
- * Called by the handler to indicate that the recents animation input consumer should be
- * enabled. This is currently used to work around an issue where registering an input consumer
- * mid-animation causes the existing motion event chain to be canceled. Instead, the caller
- * may register the recents animation input consumer prior to starting the recents animation
- * and then enable it mid-animation to start receiving touch events.
- */
- @UnsupportedAppUsage
- void setInputConsumerEnabled(boolean enabled);
-
- /**
- * Informs the system whether the animation targets passed into
- * IRecentsAnimationRunner.onAnimationStart are currently behind the system bars. If they are,
- * they can control the SystemUI flags, otherwise the SystemUI flags from home activity will be
- * taken.
- */
- @UnsupportedAppUsage
- void setAnimationTargetsBehindSystemBars(boolean behindSystemBars);
-
- /**
- * Clean up the screenshot of previous task which was created during recents animation that
- * was cancelled by a stack order change.
- *
- * @see {@link IRecentsAnimationRunner#onAnimationCanceled}
- */
- void cleanupScreenshot();
-
- /**
- * Set a state for controller whether would like to cancel recents animations with deferred
- * task screenshot presentation.
- *
- * When we cancel the recents animation due to a stack order change, we can't just cancel it
- * immediately as it would lead to a flicker in Launcher if we just remove the task from the
- * leash. Instead we screenshot the previous task and replace the child of the leash with the
- * screenshot, so that Launcher can still control the leash lifecycle & make the next app
- * transition animate smoothly without flickering.
- *
- * @param defer When set {@code true}, means that the recents animation will defer canceling the
- * animation when a stack order change is triggered until the subsequent app
- * transition start and skip previous task's animation.
- * When set to {@code false}, means that the recents animation will be canceled
- * immediately when the stack order changes.
- * @param screenshot When set {@code true}, means that the system will take previous task's
- * screenshot and replace the contents of the leash with it when the next app
- * transition starting. The runner must call #cleanupScreenshot() to end the
- * recents animation.
- * When set to {@code false}, means that the system will simply wait for the
- * next app transition start to immediately cancel the recents animation. This
- * can be useful when you want an immediate transition into a state where the
- * task is shown in the home/recents activity (without waiting for a
- * screenshot).
- *
- * @see #cleanupScreenshot()
- * @see IRecentsAnimationRunner#onCancelled
- */
- void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot);
-
- /**
- * Sets a state for controller to decide which surface is the destination when the recents
- * animation is cancelled through fail safe mechanism.
- */
- void setWillFinishToHome(boolean willFinishToHome);
-
- /**
- * Stops controlling a task that is currently controlled by this recents animation.
- *
- * This method should be called when a task that has been received via {@link #onAnimationStart}
- * or {@link #onTaskAppeared} is no longer needed. After calling this method, the task will
- * either disappear from the screen, or jump to its final position in case it was the top task.
- *
- * @param taskId Id of the Task target to remove
- * @return {@code true} when target removed successfully, {@code false} otherwise.
- */
- boolean removeTask(int taskId);
-
- /**
- * Detach navigation bar from app.
- *
- * The system reparents the leash of navigation bar to the app when the recents animation starts
- * and Launcher should call this method to let system restore the navigation bar to its
- * original position when the quick switch gesture is finished and will run the fade-in
- * animation If {@param moveHomeToTop} is {@code true}. Otherwise, restore the navigtation bar
- * without animation.
- *
- * @param moveHomeToTop if {@code true}, the home activity should be moved to the top.
- * Otherwise, the home activity is hidden and the user is returned to the
- * app.
- */
- void detachNavigationBarFromApp(boolean moveHomeToTop);
-
- /**
- * Used for animating the navigation bar during app launch from recents in live tile mode.
- *
- * First fade out the navigation bar at the bottom of the display and then fade in to the app.
- *
- * @param duration the duration of the app launch animation
- */
- void animateNavigationBarToApp(long duration);
-
- /**
- * Hand off the ongoing animation of a set of remote targets, to be run by another handler using
- * the given starting parameters.
- *
- * Once the handoff is complete, operations on the old leashes for the given targets as well as
- * callbacks will become no-ops.
- *
- * The number of targets MUST match the number of states, and each state MUST match the target
- * at the same index.
- */
- oneway void handOffAnimation(in RemoteAnimationTarget[] targets,
- in WindowAnimationState[] states);
-}
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index c7e93c1..b801465 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -149,15 +149,17 @@
private void setPreCommitProgress(float progress) {
if (isHideAnimationInProgress()) return;
+ setInterpolatedProgress(BACK_GESTURE.getInterpolation(progress) * PEEK_FRACTION);
+ }
+
+ private void setInterpolatedProgress(float progress) {
if (mWindowInsetsAnimationController != null) {
float hiddenY = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
float shownY = mWindowInsetsAnimationController.getShownStateInsets().bottom;
float imeHeight = shownY - hiddenY;
- float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
- int newY = (int) (imeHeight - interpolatedProgress * (imeHeight * PEEK_FRACTION));
+ int newY = (int) (imeHeight - progress * imeHeight);
if (mStartRootScrollY != 0) {
- mViewRoot.setScrollY(
- (int) (mStartRootScrollY * (1 - interpolatedProgress * PEEK_FRACTION)));
+ mViewRoot.setScrollY((int) (mStartRootScrollY * (1 - progress)));
}
mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, newY), 1f,
progress);
@@ -171,21 +173,14 @@
return;
}
mTriggerBack = triggerBack;
- int currentBottomInset = mWindowInsetsAnimationController.getCurrentInsets().bottom;
- int targetBottomInset;
- if (triggerBack) {
- targetBottomInset = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
- } else {
- targetBottomInset = mWindowInsetsAnimationController.getShownStateInsets().bottom;
- }
- mPostCommitAnimator = ValueAnimator.ofFloat(currentBottomInset, targetBottomInset);
+ float targetProgress = triggerBack ? 1f : 0f;
+ mPostCommitAnimator = ValueAnimator.ofFloat(
+ BACK_GESTURE.getInterpolation(mLastProgress) * PEEK_FRACTION, targetProgress);
mPostCommitAnimator.setInterpolator(
triggerBack ? STANDARD_ACCELERATE : EMPHASIZED_DECELERATE);
mPostCommitAnimator.addUpdateListener(animation -> {
- int bottomInset = (int) ((float) animation.getAnimatedValue());
if (mWindowInsetsAnimationController != null) {
- mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, bottomInset),
- 1f, animation.getAnimatedFraction());
+ setInterpolatedProgress((float) animation.getAnimatedValue());
} else {
reset();
}
@@ -213,14 +208,8 @@
notifyHideIme();
// requesting IME as invisible during post-commit
mInsetsController.setRequestedVisibleTypes(0, ime());
- // Changes the animation state. This also notifies RootView of changed insets, which
- // causes it to reset its scrollY to 0f (animated) if it was panned
mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
}
- if (mStartRootScrollY != 0 && !triggerBack) {
- // This causes RootView to update its scroll back to the panned position
- mInsetsController.getHost().notifyInsetsChanged();
- }
}
private void notifyHideIme() {
@@ -282,6 +271,10 @@
return mPostCommitAnimator != null && mTriggerBack;
}
+ boolean isAnimationInProgress() {
+ return mIsPreCommitAnimationInProgress || mWindowInsetsAnimationController != null;
+ }
+
/**
* Dump information about this ImeBackAnimationController
*
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 6343313..e90b1c0 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -70,7 +70,14 @@
"ImeInsetsSourceConsumer#onAnimationFinished",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
- boolean insetsChanged = super.onAnimationStateChanged(running);
+ boolean insetsChanged = false;
+ if (Flags.predictiveBackIme() && !running && isShowRequested()
+ && mAnimationState == ANIMATION_STATE_HIDE) {
+ // A user controlled hide animation may have ended in the shown state (e.g.
+ // cancelled predictive back animation) -> Insets need to be reset to shown.
+ insetsChanged |= applyLocalVisibilityOverride();
+ }
+ insetsChanged |= super.onAnimationStateChanged(running);
if (running && !isShowRequested()
&& mController.isPredictiveBackImeHideAnimInProgress()) {
// IME predictive back animation switched from pre-commit to post-commit.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 7c8cd93..8fdf91a 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1197,7 +1197,8 @@
pendingRequest.listener, null /* frame */, true /* fromIme */,
pendingRequest.mInsetsAnimationSpec,
pendingRequest.animationType, pendingRequest.layoutInsetsDuringAnimation,
- pendingRequest.useInsetsAnimationThread, statsToken);
+ pendingRequest.useInsetsAnimationThread, statsToken,
+ false /* fromPredictiveBack */);
}
@Override
@@ -1307,7 +1308,10 @@
WindowInsetsAnimationControlListener listener,
boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
@AnimationType int animationType, boolean fromPredictiveBack) {
- if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) {
+ if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0
+ || (fromPredictiveBack && ((mRequestedVisibleTypes & ime()) == 0))) {
+ // abort if insets are uncontrollable or if control request is from predictive back but
+ // there is already a hide anim in progress
listener.onCancelled(null);
return;
}
@@ -1330,7 +1334,7 @@
// TODO(b/342111149): Create statsToken here once ImeTracker#onStart becomes async.
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, spec,
animationType, getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
- false /* useInsetsAnimationThread */, null);
+ false /* useInsetsAnimationThread */, null, fromPredictiveBack);
}
private void controlAnimationUnchecked(@InsetsType int types,
@@ -1338,7 +1342,8 @@
WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+ boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken,
+ boolean fromPredictiveBack) {
final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
// Basically, we accept the requested visibilities from the upstream callers...
@@ -1348,7 +1353,7 @@
// rejecting showing IME.
controlAnimationUncheckedInner(types, cancellationSignal, listener, frame, fromIme,
insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
- useInsetsAnimationThread, statsToken);
+ useInsetsAnimationThread, statsToken, fromPredictiveBack);
// We are finishing setting the requested visible types. Report them to the server
// and/or the app.
@@ -1360,7 +1365,8 @@
WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+ boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken,
+ boolean fromPredictiveBack) {
if ((types & mTypesBeingCancelled) != 0) {
final boolean monitoredAnimation =
animationType == ANIMATION_TYPE_SHOW || animationType == ANIMATION_TYPE_HIDE;
@@ -1446,7 +1452,7 @@
}
} else {
Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
- fromIme, types, controls, animationType, statsToken);
+ fromIme, types, controls, animationType, statsToken, fromPredictiveBack);
typesReady = typesReadyPair.first;
boolean imeReady = typesReadyPair.second;
if (DEBUG) {
@@ -1582,7 +1588,7 @@
*/
private Pair<Integer, Boolean> collectSourceControls(boolean fromIme, @InsetsType int types,
SparseArray<InsetsSourceControl> controls, @AnimationType int animationType,
- @Nullable ImeTracker.Token statsToken) {
+ @Nullable ImeTracker.Token statsToken, boolean fromPredictiveBack) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS);
@@ -1594,7 +1600,8 @@
continue;
}
boolean show = animationType == ANIMATION_TYPE_SHOW
- || animationType == ANIMATION_TYPE_USER;
+ || (animationType == ANIMATION_TYPE_USER
+ && (!fromPredictiveBack || !mHost.hasAnimationCallbacks()));
boolean canRun = true;
if (show) {
// Show request
@@ -1617,7 +1624,8 @@
break;
}
} else {
- consumer.requestHide(fromIme, statsToken);
+ consumer.requestHide(fromIme
+ || (fromPredictiveBack && mHost.hasAnimationCallbacks()), statsToken);
}
if (!canRun) {
if (WARN) Log.w(TAG, String.format(
@@ -1672,9 +1680,10 @@
private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
@InsetsType int types, boolean fromPredictiveBack) {
- if (fromPredictiveBack) {
- // When insets are animated by predictive back, we want insets to be shown to prevent a
- // jump cut from shown to hidden at the start of the predictive back animation
+ if (fromPredictiveBack && !mHost.hasAnimationCallbacks()) {
+ // When insets are animated by predictive back and the app does not have an animation
+ // callback, we want insets to be shown to prevent a jump cut from shown to hidden at
+ // the start of the predictive back animation
return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
}
// Generally, we want to layout the opposite of the current state. This is to make animation
@@ -2021,7 +2030,8 @@
listener /* insetsAnimationSpec */,
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
- !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken);
+ !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken,
+ false /* fromPredictiveBack */);
}
/**
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 815fd1c..dd950e8 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -540,8 +540,6 @@
// Assumes they have the exact duration.
mDurationPerFrame = animationDrawable.getDuration(0);
mBitmapFrames = new Bitmap[frames - 1];
- final int width = drawable.getIntrinsicWidth();
- final int height = drawable.getIntrinsicHeight();
final boolean isVectorAnimation = drawable instanceof VectorDrawable;
mDrawNativeDropShadow = isVectorAnimation;
for (int i = 1; i < frames; ++i) {
@@ -556,9 +554,6 @@
+ "is a different type from the others. All frames should be the "
+ "same type.");
}
- // TODO(b/361232935): Check when bitmap size of the ith frame is different
- // drawableFrame.getIntrinsicWidth() != width ||
- // drawableFrame.getIntrinsicHeight() != height
if (isVectorAnimation) {
drawableFrame = getBitmapDrawableFromVectorDrawable(resources,
(VectorDrawable) drawableFrame, pointerScale);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e81f32e..0ed0e60 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -141,7 +141,6 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.Vibrator;
import android.service.credentials.CredentialProviderService;
import android.sysprop.DisplayProperties;
import android.text.InputType;
@@ -372,7 +371,7 @@
* </tr>
* <tr>
* <td><code>{@link #onTouchEvent(MotionEvent)}</code></td>
- * <td>Called when a touch screen motion event occurs.
+ * <td>Called when a motion event occurs with pointers down on the view.
* </td>
* </tr>
* <tr>
@@ -17874,7 +17873,13 @@
}
/**
- * Implement this method to handle touch screen motion events.
+ * Implement this method to handle pointer events.
+ * <p>
+ * This method is called to handle motion events where pointers are down on
+ * the view. For example, this could include touchscreen touches, stylus
+ * touches, or click-and-drag events from a mouse. However, it is not called
+ * for motion events that do not involve pointers being down, such as hover
+ * events or mouse scroll wheel movements.
* <p>
* If this method is used to detect click actions, it is recommended that
* the actions be performed by implementing and calling
@@ -27378,6 +27383,29 @@
}
/**
+ * Modifiers the input matrix such that it maps root view's coordinates to view-local
+ * coordinates.
+ *
+ * @param matrix input matrix to modify
+ * @hide
+ */
+ public void transformMatrixRootToLocal(@NonNull Matrix matrix) {
+ final ViewParent parent = mParent;
+ if (parent instanceof final View vp) {
+ vp.transformMatrixRootToLocal(matrix);
+ matrix.postTranslate(vp.mScrollX, vp.mScrollY);
+ }
+ // This method is different from transformMatrixToLocal that it doesn't perform any
+ // transformation for ViewRootImpl
+
+ matrix.postTranslate(-mLeft, -mTop);
+
+ if (!hasIdentityMatrix()) {
+ matrix.postConcat(getInverseMatrix());
+ }
+ }
+
+ /**
* @hide
*/
@ViewDebug.ExportedProperty(category = "layout", indexMapping = {
@@ -34156,7 +34184,8 @@
* REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
* Keep in mind that the preferred frame rate affects the frame rate for the next frame,
* so use this method carefully. It's important to note that the preference is valid as
- * long as the View is invalidated.
+ * long as the View is invalidated. Please also be aware that the requested frame rate
+ * will not propagate to child views when this API is used on a ViewGroup.
*
* @param frameRate the preferred frame rate of the view.
*/
@@ -34175,6 +34204,8 @@
* REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
* REQUESTED_FRAME_RATE_CATEGORY_NORMAL, and REQUESTED_FRAME_RATE_CATEGORY_HIGH.
* Note that the frame rate value is valid as long as the View is invalidated.
+ * Please also be aware that the requested frame rate will not propagate to
+ * child views when this API is used on a ViewGroup.
*
* @return REQUESTED_FRAME_RATE_CATEGORY_DEFAULT by default,
* or value passed to {@link #setRequestedFrameRate(float)}.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index fb2b8b9..63bf392 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -373,6 +373,7 @@
private final int mMaximumDrawingCacheSize;
private final int mOverscrollDistance;
private final int mOverflingDistance;
+ private final boolean mViewTouchScreenHapticScrollFeedbackEnabled;
@UnsupportedAppUsage
private final boolean mFadingMarqueeEnabled;
private final long mGlobalActionsKeyTimeout;
@@ -437,6 +438,7 @@
mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND;
mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND;
mPreferKeepClearForFocusEnabled = false;
+ mViewTouchScreenHapticScrollFeedbackEnabled = false;
}
/**
@@ -588,6 +590,12 @@
mViewBasedRotaryEncoderScrollHapticsEnabledConfig =
res.getBoolean(
com.android.internal.R.bool.config_viewBasedRotaryEncoderHapticsEnabled);
+ mViewTouchScreenHapticScrollFeedbackEnabled =
+ Flags.enableTouchScrollFeedback()
+ ? res.getBoolean(
+ com.android.internal.R.bool
+ .config_viewTouchScreenHapticScrollFeedbackEnabled)
+ : false;
}
/**
@@ -1285,6 +1293,10 @@
return mRotaryEncoderHapticScrollFeedbackEnabled;
}
+ if ((source & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+ return mViewTouchScreenHapticScrollFeedbackEnabled;
+ }
+
return false;
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3b5286a..2237417 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4504,6 +4504,11 @@
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
+ if (child == null) {
+ throw new IllegalStateException(getClass().getSimpleName() + " contains null " +
+ "child at index " + i + " when traversal in dispatchGetDisplayList," +
+ " the view may have been removed.");
+ }
if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
recreateChildDisplayList(child);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0e1625a..e10cc28 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4345,6 +4345,7 @@
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, "view not visible");
+ mHasPendingTransactions = false;
} else if (cancelAndRedraw) {
if (!mWasLastDrawCanceled) {
logAndTrace("Canceling draw."
@@ -4372,6 +4373,7 @@
if (!performDraw(mActiveSurfaceSyncGroup)) {
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, mLastPerformDrawSkippedReason);
+ mHasPendingTransactions = false;
}
}
mWasLastDrawCanceled = cancelAndRedraw;
@@ -4388,7 +4390,14 @@
mReportNextDraw = false;
mLastReportNextDrawReason = null;
mActiveSurfaceSyncGroup = null;
- mHasPendingTransactions = false;
+ if (mHasPendingTransactions) {
+ // TODO: We shouldn't ever actually hit this, it means mPendingTransaction wasn't
+ // merged with a sync group or BLASTBufferQueue before making it to this point
+ // But better a one or two frame flicker than steady-state broken from dropping
+ // whatever is in this transaction
+ mPendingTransaction.apply();
+ mHasPendingTransactions = false;
+ }
mSyncBuffer = false;
if (isInWMSRequestedSync()) {
mWmsRequestSyncGroup.markSyncReady();
@@ -5305,6 +5314,7 @@
private void registerCallbackForPendingTransactions() {
Transaction t = new Transaction();
t.merge(mPendingTransaction);
+ mHasPendingTransactions = false;
registerRtFrameCallback(new FrameDrawingCallback() {
@Override
@@ -5384,6 +5394,7 @@
if (!usingAsyncReport && mHasPendingTransactions) {
pendingTransaction = new Transaction();
pendingTransaction.merge(mPendingTransaction);
+ mHasPendingTransactions = false;
} else {
pendingTransaction = null;
}
@@ -6094,6 +6105,12 @@
}
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
+ if (mImeBackAnimationController.isAnimationInProgress()) {
+ // IME predictive back animation is currently in progress which means that scrollY is
+ // currently controlled by ImeBackAnimationController.
+ return false;
+ }
+
final Rect ci = mAttachInfo.mContentInsets;
final Rect vi = mAttachInfo.mVisibleInsets;
int scrollY = 0;
@@ -9936,6 +9953,7 @@
}
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, "shutting down VRI");
+ mHasPendingTransactions = false;
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -12595,6 +12613,7 @@
if (mHasPendingTransactions) {
t = new Transaction();
t.merge(mPendingTransaction);
+ mHasPendingTransactions = false;
} else {
t = null;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 67a207e..5b39f62 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -107,6 +107,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -2371,7 +2372,7 @@
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
/**
- * Window type: the drag-and-drop pseudowindow. There is only one
+ * Window type: the drag-and-drop pseudowindow. There is only one
* drag layer (at most), and it is placed on top of all other windows.
* In multiuser systems shows only on the owning user's window.
* @hide
@@ -2381,7 +2382,7 @@
/**
* Window type: panel that slides out from over the status bar
* In multiuser systems shows on all users' windows. These windows
- * are displayed on top of the stauts bar and any {@link #TYPE_STATUS_BAR_PANEL}
+ * are displayed on top of the status bar and any {@link #TYPE_STATUS_BAR_PANEL}
* windows.
* @hide
*/
@@ -4649,6 +4650,30 @@
public InsetsFrameProvider[] providedInsets;
/**
+ * Sets the insets to be provided by the window.
+ *
+ * @param insetsParams The parameters for the insets to be provided by the window.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_STATUS_BAR_AND_INSETS)
+ @SystemApi
+ public void setInsetsParams(@NonNull List<InsetsParams> insetsParams) {
+ if (insetsParams.isEmpty()) {
+ providedInsets = null;
+ } else {
+ providedInsets = new InsetsFrameProvider[insetsParams.size()];
+ for (int i = 0; i < insetsParams.size(); ++i) {
+ final InsetsParams params = insetsParams.get(i);
+ providedInsets[i] =
+ new InsetsFrameProvider(/* owner= */ this, /* index= */ i,
+ params.getType())
+ .setInsetsSize(params.getInsetsSize());
+ }
+ }
+ }
+
+ /**
* Specifies which {@link InsetsType}s should be forcibly shown. The types shown by this
* method won't affect the app's layout. This field only takes effects if the caller has
* {@link android.Manifest.permission#STATUS_BAR_SERVICE} or the caller has the same uid as
@@ -6117,6 +6142,56 @@
}
/**
+ * Specifies the parameters of the insets provided by a window.
+ *
+ * @see WindowManager.LayoutParams#setInsetsParams(List)
+ * @see android.graphics.Insets
+ *
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_STATUS_BAR_AND_INSETS)
+ @SystemApi
+ public static class InsetsParams {
+
+ private final @InsetsType int mType;
+ private @Nullable Insets mInsets;
+
+ /**
+ * Creates an instance of InsetsParams.
+ *
+ * @param type the type of insets to provide, e.g. {@link WindowInsets.Type#statusBars()}.
+ * @see WindowInsets.Type
+ */
+ public InsetsParams(@InsetsType int type) {
+ mType = type;
+ }
+
+ /**
+ * Sets the size of the provided insets. If {@code null}, then the provided insets will
+ * have the same size as the window frame.
+ */
+ public @NonNull InsetsParams setInsetsSize(@Nullable Insets insets) {
+ mInsets = insets;
+ return this;
+ }
+
+ /**
+ * Returns the type of provided insets.
+ */
+ public @InsetsType int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the size of the provided insets. May be {@code null} if the provided insets have
+ * the same size as the window frame.
+ */
+ public @Nullable Insets getInsetsSize() {
+ return mInsets;
+ }
+ }
+
+ /**
* Holds the WM lock for the specified amount of milliseconds.
* Intended for use by the tests that need to imitate lock contention.
* The token should be obtained by
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a4cea33..2b7cf42 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1051,6 +1051,52 @@
}
/**
+ * Registers callback for when user initialization has completed.
+ * Does nothing if the same callback is already registered.
+ *
+ * @param callback The callback to be registered
+ * @hide
+ */
+ public void registerUserInitializationCompleteCallback(
+ @NonNull IUserInitializationCompleteCallback callback) {
+ IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.registerUserInitializationCompleteCallback(callback);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while registering userInitializationCompleteCallback. ", re);
+ }
+ }
+
+ /**
+ * Unregisters callback for when user initialization has completed.
+ *
+ * @param callback The callback to be unregistered
+ * @hide
+ */
+ public void unregisterUserInitializationCompleteCallback(
+ @NonNull IUserInitializationCompleteCallback callback) {
+ IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.unregisterUserInitializationCompleteCallback(callback);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG,
+ "Error while unregistering userInitializationCompleteCallback. ", re);
+ }
+ }
+
+ /**
* Whether the current accessibility request comes from an
* {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
* property set to true.
@@ -1728,7 +1774,8 @@
}
/**
- * Notifies that the accessibility button in the system's navigation area has been clicked
+ * Notifies that the accessibility button in the system's navigation area has been clicked,
+ * or a gesture shortcut input has been performed.
*
* @param displayId The logical display id.
* @hide
@@ -1739,7 +1786,8 @@
}
/**
- * Perform the accessibility button for the given target which is assigned to the button.
+ * Perform the accessibility button or gesture
+ * for the given target which is assigned to the button.
*
* @param displayId displayId The logical display id.
* @param targetName The flattened {@link ComponentName} string or the class name of a system
@@ -1764,6 +1812,31 @@
}
/**
+ * Notifies that a shortcut was long-clicked.
+ * This displays the dialog used to select which target the given shortcut will use,
+ * from its list of targets.
+ * The current shortcut type is determined by the current navigation mode.
+ *
+ * @param displayId The id of the display to show the dialog on.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.STATUS_BAR_SERVICE)
+ public void notifyAccessibilityButtonLongClicked(int displayId) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.notifyAccessibilityButtonLongClicked(displayId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while dispatching accessibility button long click. ", re);
+ }
+ }
+
+ /**
* Notifies that the visibility of the accessibility button in the system's navigation area
* has changed.
*
@@ -2049,9 +2122,7 @@
* {@link android.view.Display#DEFAULT_DISPLAY}, is or lower than
* {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed.
*
- * @throws SecurityException if the app does not hold the
- * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
- * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
+ * @throws SecurityException if the app does not hold the required permissions.
*
* @hide
*/
@@ -2079,9 +2150,7 @@
*
* @return {@code true} if the proxy is successfully unregistered.
*
- * @throws SecurityException if the app does not hold the
- * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
- * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
+ * @throws SecurityException if the app does not hold the required permissions.
*
* @hide
*/
@@ -2134,8 +2203,8 @@
try {
return service.startFlashNotificationSequence(context.getOpPackageName(),
reason, mBinder);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while start flash notification sequence", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while start flash notification sequence", e);
return false;
}
}
@@ -2164,8 +2233,8 @@
try {
return service.stopFlashNotificationSequence(context.getOpPackageName());
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while stop flash notification sequence", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while stop flash notification sequence", e);
return false;
}
}
@@ -2192,8 +2261,8 @@
try {
return service.startFlashNotificationEvent(context.getOpPackageName(),
reason, reasonPkg);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while start flash notification event", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while start flash notification event", e);
return false;
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 72a1fe4..e04fa15 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -29,6 +29,7 @@
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.AccessibilityWindowAttributes;
import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import android.view.InputEvent;
import android.view.IWindow;
import android.view.MagnificationSpec;
@@ -87,6 +88,8 @@
@EnforcePermission("STATUS_BAR_SERVICE")
void notifyAccessibilityButtonClicked(int displayId, String targetName);
+ @EnforcePermission("STATUS_BAR_SERVICE")
+ void notifyAccessibilityButtonLongClicked(int displayId);
@EnforcePermission("STATUS_BAR_SERVICE")
void notifyAccessibilityButtonVisibilityChanged(boolean available);
@@ -156,13 +159,13 @@
@EnforcePermission("INJECT_EVENTS")
void injectInputEventToInputFilter(in InputEvent event);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean startFlashNotificationSequence(String opPkg, int reason, IBinder token);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean stopFlashNotificationSequence(String opPkg);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean startFlashNotificationEvent(String opPkg, int reason, String reasonPkg);
@RequiresNoPermission
@@ -192,4 +195,10 @@
@EnforcePermission("MANAGE_ACCESSIBILITY")
Bundle getA11yFeatureToTileMap(int userId);
+
+ @RequiresNoPermission
+ void registerUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback);
+
+ @RequiresNoPermission
+ void unregisterUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback);
}
diff --git a/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl
new file mode 100644
index 0000000..fe6c8e2
--- /dev/null
+++ b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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 android.view.accessibility;
+
+/**
+ * A callback for when a new user finishes initializing
+ * NOTE: Must remain a oneway interface, as it is called from system_server while holding a lock.
+ * oneway allows it to return immediately and not hold the lock for longer than is necessary.
+ * @hide
+ */
+
+oneway interface IUserInitializationCompleteCallback {
+
+ /**
+ * Called when a user initialization completes.
+ *
+ * @param userId the id of the initialized user
+ */
+ @RequiresNoPermission
+ void onUserInitializationComplete(int userId);
+}
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/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index 338037f..e9c85684 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -14,4 +14,11 @@
name: "use_view_based_rotary_encoder_scroll_haptics"
description: "If enabled, the rotary encoder scroll haptic implementation in the View class will be used, and the HapticScrollFeedbackProvider logic for rotary encoder haptic will be muted."
bug: "299587011"
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "toolkit"
+ name: "enable_touch_scroll_feedback"
+ description: "Enables touchscreen haptic scroll feedback"
+ bug: "331830899"
+}
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 2f515fe..3a008aa 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -323,14 +323,14 @@
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, @Nullable ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
return service.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
- resultReceiver, reason);
+ resultReceiver, reason, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -339,14 +339,15 @@
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
+ boolean async) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
return service.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
- reason);
+ reason, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -407,7 +408,7 @@
@Nullable IRemoteInputConnection remoteInputConnection,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean useAsyncShowHideMethod) {
final IInputMethodManager service = getService();
if (service == null) {
return -1;
@@ -416,7 +417,7 @@
service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
- imeDispatcher, advanceAngGetStartInputSequenceNumber());
+ imeDispatcher, advanceAngGetStartInputSequenceNumber(), useAsyncShowHideMethod);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 23d7732..2f649c2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -56,6 +56,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.app.PropertyInvalidatedCache;
+import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -441,6 +442,36 @@
public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id.
/**
+ * Use async method for {@link InputMethodManager#showSoftInput(View, int, ResultReceiver)},
+ * {@link InputMethodManager#showSoftInput(View, int)} and
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int, ResultReceiver)},
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} for apps targeting V+.
+ * <p>
+ * Apps can incorrectly rely on {@link InputMethodManager#showSoftInput(View, int)} and
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} method return type
+ * to interpret result of a request rather than relying on {@link ResultReceiver}. The return
+ * type of the method was never documented to have accurate info of visibility but few apps
+ * incorrectly rely on it.
+ * <p>
+ * Starting Android V, we use async calls into system_server which returns {@code true} if
+ * method call was made but return type doesn't guarantee execution.
+ * Apps targeting older versions will fallback to existing behavior of calling synchronous
+ * methods which had undocumented result in return type.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final long USE_ASYNC_SHOW_HIDE_METHOD = 352594277L; // This is a bug id.
+
+ /**
+ * Version-gating is guarded by bug-fix flag.
+ */
+ private static final boolean ASYNC_SHOW_HIDE_METHOD_ENABLED =
+ !Flags.compatchangeForZerojankproxy()
+ || CompatChanges.isChangeEnabled(USE_ASYNC_SHOW_HIDE_METHOD);
+
+ /**
* If {@code true}, avoid calling the
* {@link com.android.server.inputmethod.InputMethodManagerService InputMethodManagerService}
* by skipping the call to {@link IInputMethodManager#startInputOrWindowGainedFocus}
@@ -2246,6 +2277,8 @@
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags) {
// Re-dispatch if there is a context mismatch.
@@ -2315,6 +2348,8 @@
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
@@ -2383,7 +2418,8 @@
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- reason);
+ reason,
+ ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
}
@@ -2426,7 +2462,8 @@
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- reason);
+ reason,
+ ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
@@ -2459,6 +2496,9 @@
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@link ResultReceiver} in
+ * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
@@ -2487,6 +2527,8 @@
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver) {
@@ -2530,7 +2572,7 @@
return true;
} else {
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken,
- statsToken, flags, resultReceiver, reason);
+ statsToken, flags, resultReceiver, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
}
@@ -2573,7 +2615,7 @@
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, view.getWindowToken(),
- statsToken, flags, null, reason);
+ statsToken, flags, null, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
@@ -3350,7 +3392,7 @@
servedInputConnection == null ? null
: servedInputConnection.asIRemoteAccessibilityInputConnection(),
view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
- mImeDispatcher);
+ mImeDispatcher, ASYNC_SHOW_HIDE_METHOD_ENABLED);
} else {
res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
@@ -3653,7 +3695,8 @@
statsToken,
HIDE_NOT_ALWAYS,
null,
- reason);
+ reason,
+ true /*async */);
}
}
@@ -3745,7 +3788,7 @@
IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken,
0 /* flags */, null /* resultReceiver */,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, true /* async */);
}
}
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index e294ee2..bae8aff 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -136,3 +136,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "compatchange_for_zerojankproxy"
+ namespace: "input_method"
+ description: "Version-gate the sync/async nature of IMM#show/hideSoftInput() when using zeroJankProxy."
+ bug: "352594277"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 1da2af4..b18dbbc 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -15,12 +15,14 @@
*/
package android.webkit;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
+import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.UserHandle;
import android.os.UserManager;
import java.util.ArrayList;
@@ -31,30 +33,31 @@
* @hide
*/
public class UserPackage {
- private final UserInfo mUserInfo;
+ private final UserHandle mUser;
private final PackageInfo mPackageInfo;
public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.TIRAMISU;
- public UserPackage(UserInfo user, PackageInfo packageInfo) {
- this.mUserInfo = user;
- this.mPackageInfo = packageInfo;
+ public UserPackage(@NonNull UserHandle user, @Nullable PackageInfo packageInfo) {
+ mUser = user;
+ mPackageInfo = packageInfo;
}
/**
* Returns a list of (User,PackageInfo) pairs corresponding to the PackageInfos for all
* device users for the package named {@param packageName}.
*/
- public static List<UserPackage> getPackageInfosAllUsers(Context context,
- String packageName, int packageFlags) {
- List<UserInfo> users = getAllUsers(context);
+ public static @NonNull List<UserPackage> getPackageInfosAllUsers(@NonNull Context context,
+ @NonNull String packageName, int packageFlags) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ List<UserHandle> users = userManager.getUserHandles(false);
List<UserPackage> userPackages = new ArrayList<UserPackage>(users.size());
- for (UserInfo user : users) {
+ for (UserHandle user : users) {
+ PackageManager pm = context.createContextAsUser(user, 0).getPackageManager();
PackageInfo packageInfo = null;
try {
- packageInfo = context.getPackageManager().getPackageInfoAsUser(
- packageName, packageFlags, user.id);
- } catch (NameNotFoundException e) {
+ packageInfo = pm.getPackageInfo(packageName, packageFlags);
+ } catch (PackageManager.NameNotFoundException e) {
}
userPackages.add(new UserPackage(user, packageInfo));
}
@@ -88,18 +91,11 @@
return packageInfo.applicationInfo.targetSdkVersion >= MINIMUM_SUPPORTED_SDK;
}
- public UserInfo getUserInfo() {
- return mUserInfo;
+ public UserHandle getUser() {
+ return mUser;
}
public PackageInfo getPackageInfo() {
return mPackageInfo;
}
-
-
- private static List<UserInfo> getAllUsers(Context context) {
- UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- return userManager.getUsers();
- }
-
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f336b5d..ffe8c80 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3088,11 +3088,11 @@
}
if (Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager == null) {
+ if (WebViewFactory.isWebViewSupported()) {
+ return WebViewUpdateManager.getInstance().getCurrentWebViewPackage();
+ } else {
return null;
}
- return manager.getCurrentWebViewPackage();
} else {
IWebViewUpdateService service = WebViewFactory.getUpdateService();
if (service == null) {
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 8501474..4c5802c 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -137,9 +137,13 @@
*/
@Deprecated
public void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor) {
- ViewRootImpl viewRootImpl = containerView.getViewRootImpl();
- if (nativeDrawGLFunctor != 0 && viewRootImpl != null) {
- viewRootImpl.detachFunctor(nativeDrawGLFunctor);
+ if (Flags.mainlineApis()) {
+ throw new UnsupportedOperationException();
+ } else {
+ ViewRootImpl viewRootImpl = containerView.getViewRootImpl();
+ if (nativeDrawGLFunctor != 0 && viewRootImpl != null) {
+ viewRootImpl.detachFunctor(nativeDrawGLFunctor);
+ }
}
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index c53a0e1..e10a398 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -208,7 +208,7 @@
public MissingWebViewPackageException(Exception e) { super(e); }
}
- private static boolean isWebViewSupported() {
+ static boolean isWebViewSupported() {
// No lock; this is a benign race as Boolean's state is final and the PackageManager call
// will always return the same value.
if (sWebViewSupported == null) {
@@ -318,7 +318,7 @@
String libraryFileName;
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
- PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+ PackageManager.GET_META_DATA);
libraryFileName = getWebViewLibrary(packageInfo.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOGTAG, "Couldn't find package " + packageName);
@@ -479,7 +479,6 @@
newPackageInfo = pm.getPackageInfo(
response.packageInfo.packageName,
PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
// Make sure that we fetch the current provider even if its not
// installed for the current user
| PackageManager.MATCH_UNINSTALLED_PACKAGES
diff --git a/core/java/android/webkit/WebViewUpdateManager.java b/core/java/android/webkit/WebViewUpdateManager.java
index dd48df9..0eb71001 100644
--- a/core/java/android/webkit/WebViewUpdateManager.java
+++ b/core/java/android/webkit/WebViewUpdateManager.java
@@ -19,12 +19,14 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
import android.content.Context;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
/** @hide */
@@ -43,8 +45,11 @@
*
* This exists for the benefit of callsites without a {@link Context}; prefer
* {@link Context#getSystemService(Class)} otherwise.
+ *
+ * This can only be used on devices with {@link PackageManager#FEATURE_WEBVIEW}.
*/
@SuppressLint("ManagerLookup") // service opts in to getSystemServiceWithNoContext()
+ @RequiresFeature(PackageManager.FEATURE_WEBVIEW)
public static @Nullable WebViewUpdateManager getInstance() {
return (WebViewUpdateManager) SystemServiceRegistry.getSystemServiceWithNoContext(
Context.WEBVIEW_UPDATE_SERVICE);
diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java
index 6f53dde..01af182 100644
--- a/core/java/android/webkit/WebViewUpdateService.java
+++ b/core/java/android/webkit/WebViewUpdateService.java
@@ -34,11 +34,11 @@
*/
public static WebViewProviderInfo[] getAllWebViewPackages() {
if (Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager == null) {
+ if (WebViewFactory.isWebViewSupported()) {
+ return WebViewUpdateManager.getInstance().getAllWebViewPackages();
+ } else {
return new WebViewProviderInfo[0];
}
- return manager.getAllWebViewPackages();
} else {
IWebViewUpdateService service = getUpdateService();
if (service == null) {
@@ -57,11 +57,11 @@
*/
public static WebViewProviderInfo[] getValidWebViewPackages() {
if (Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager == null) {
+ if (WebViewFactory.isWebViewSupported()) {
+ return WebViewUpdateManager.getInstance().getValidWebViewPackages();
+ } else {
return new WebViewProviderInfo[0];
}
- return manager.getValidWebViewPackages();
} else {
IWebViewUpdateService service = getUpdateService();
if (service == null) {
@@ -80,11 +80,11 @@
*/
public static String getCurrentWebViewPackageName() {
if (Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager == null) {
+ if (WebViewFactory.isWebViewSupported()) {
+ return WebViewUpdateManager.getInstance().getCurrentWebViewPackageName();
+ } else {
return null;
}
- return manager.getCurrentWebViewPackageName();
} else {
IWebViewUpdateService service = getUpdateService();
if (service == null) {
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index defe61e..b21a490c 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -9,3 +9,12 @@
bug: "319292658"
is_fixed_read_only: true
}
+
+flag {
+ name: "mainline_apis"
+ is_exported: true
+ namespace: "webview"
+ description: "New APIs required by WebViewBootstrap mainline module"
+ bug: "310653407"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ab6b512..3c854ea 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.flags.Flags.enableTouchScrollFeedback;
+import static android.view.flags.Flags.scrollFeedbackApi;
import static android.view.flags.Flags.viewVelocityApi;
import android.annotation.ColorInt;
@@ -82,7 +84,6 @@
import android.view.autofill.AutofillId;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
-import android.view.flags.Flags;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -3703,7 +3704,6 @@
// If it's non-null, we're already in a scroll.
mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");
}
-
if (y != mLastY) {
// We may be here after stopping a fling and continuing to scroll.
// If so, we haven't disallowed intercepting touch events yet.
@@ -3735,8 +3735,15 @@
boolean atEdge = false;
if (incrementalDeltaY != 0) {
atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
- }
+ // TODO: b/360198915 - Add unit testing for using ScrollFeedbackProvider
+ if (enableTouchScrollFeedback()) {
+ initHapticScrollFeedbackProviderIfNotExists();
+ mHapticScrollFeedbackProvider.onScrollProgress(
+ vtev.getDeviceId(), vtev.getSource(), MotionEvent.AXIS_Y,
+ incrementalDeltaY);
+ }
+ }
// Check to see if we have bumped into the scroll limit
motionView = this.getChildAt(motionIndex);
if (motionView != null) {
@@ -3745,7 +3752,6 @@
final int motionViewRealTop = motionView.getTop();
if (atEdge) {
// Apply overscroll
-
int overscroll = -incrementalDeltaY -
(motionViewRealTop - motionViewPrevTop);
if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
@@ -3772,6 +3778,15 @@
mDirection = 0; // Reset when entering overscroll.
mTouchMode = TOUCH_MODE_OVERSCROLL;
}
+
+ if (enableTouchScrollFeedback()) {
+ initHapticScrollFeedbackProviderIfNotExists();
+ mHapticScrollFeedbackProvider.onScrollLimit(
+ vtev.getDeviceId(), vtev.getSource(),
+ MotionEvent.AXIS_Y,
+ /* isStart= */ incrementalDeltaY > 0);
+ }
+
if (incrementalDeltaY > 0) {
mEdgeGlowTop.onPullDistance((float) -overscroll / getHeight(),
(float) x / getWidth());
@@ -3981,7 +3996,6 @@
if (mFastScroll != null && mFastScroll.onTouchEvent(ev)) {
return true;
}
-
initVelocityTrackerIfNotExists();
final MotionEvent vtev = MotionEvent.obtain(ev);
@@ -4520,7 +4534,7 @@
final int overscrollMode = getOverScrollMode();
if (!trackMotionScroll(delta, delta)) {
- if (Flags.scrollFeedbackApi()) {
+ if (scrollFeedbackApi()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollProgress(
event.getDeviceId(), event.getSource(), axis, delta);
@@ -4536,7 +4550,7 @@
float overscroll = (delta - (motionViewRealTop - motionViewPrevTop))
/ ((float) getHeight());
boolean hitTopLimit = delta > 0;
- if (Flags.scrollFeedbackApi()) {
+ if (scrollFeedbackApi()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollLimit(
event.getDeviceId(), event.getSource(), axis,
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index eb35817..89ea852 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -153,6 +153,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -712,6 +713,24 @@
}
public abstract void writeToParcel(Parcel dest, int flags);
+
+ /**
+ * Override to return true if this Action can be serialized to Protobuf, and implement
+ * writeToProto / createFromProto.
+ *
+ * If this returns false, then the action will be omitted from RemoteViews previews created
+ * with createPreviewFromProto / writePreviewToProto.
+ *
+ * Because Parcelables should not be serialized to disk, any action that contains an Intent,
+ * PendingIntent, or Bundle should return false here.
+ */
+ public boolean canWriteToProto() {
+ return false;
+ }
+
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ throw new UnsupportedOperationException();
+ }
}
/**
@@ -1447,6 +1466,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ context.unbindService(this);
+ }
+
+ @Override
public void onServiceDisconnected(ComponentName componentName) { }
});
@@ -1501,6 +1525,7 @@
switch (in.getFieldNumber()) {
case (int) RemoteViewsProto.RemoteCollectionCache.ENTRIES:
final LongSparseArray<Object> entry = new LongSparseArray<>();
+
final long entryToken = in.start(
RemoteViewsProto.RemoteCollectionCache.ENTRIES);
while (in.nextField() != NO_MORE_FIELDS) {
@@ -1528,10 +1553,12 @@
}
}
in.end(entryToken);
+
checkContainsKeys(entry,
new long[]{RemoteViewsProto.RemoteCollectionCache.Entry.ID,
RemoteViewsProto.RemoteCollectionCache.Entry.URI,
RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS});
+
entries.add(entry);
break;
default:
@@ -2242,6 +2269,62 @@
public int getActionTag() {
return BITMAP_REFLECTION_ACTION_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION);
+ out.write(RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME, mMethodName);
+ out.write(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID, mBitmapId);
+ out.end(token);
+ }
+ }
+
+ private PendingResources<Action> createFromBitmapReflectionActionFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.BitmapReflectionAction.VIEW_ID:
+ values.put(RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
+ in.readString(RemoteViewsProto.BitmapReflectionAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.BitmapReflectionAction.METHOD_NAME:
+ values.put(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME,
+ in.readString(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME));
+ break;
+ case (int) RemoteViewsProto.BitmapReflectionAction.BITMAP_ID:
+ values.put(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID,
+ in.readInt(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
+ RemoteViewsProto.BitmapReflectionAction.METHOD_NAME});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.BitmapReflectionAction.VIEW_ID);
+ return new BitmapReflectionAction(viewId,
+ (String) values.get(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME),
+ rootData.mBitmapCache.getBitmapForId(
+ (int) values.get(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID,
+ 0)));
+ };
+
}
/**
@@ -2555,6 +2638,268 @@
public int getActionTag() {
return REFLECTION_ACTION_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.REFLECTION_ACTION);
+ out.write(RemoteViewsProto.ReflectionAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.ReflectionAction.METHOD_NAME, mMethodName);
+ out.write(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE, mType);
+ if (this.mValue != null) {
+ switch (this.mType) {
+ case BOOLEAN:
+ // ProtoOutputStream will omit this write if the value is false
+ out.write(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE,
+ (boolean) this.mValue);
+ break;
+ case BYTE:
+ out.write(RemoteViewsProto.ReflectionAction.BYTE_VALUE,
+ new byte[]{(byte) this.mValue});
+ break;
+ case SHORT:
+ out.write(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
+ (short) this.mValue);
+ break;
+ case INT:
+ out.write(RemoteViewsProto.ReflectionAction.INT_VALUE, (int) this.mValue);
+ break;
+ case LONG:
+ out.write(RemoteViewsProto.ReflectionAction.LONG_VALUE, (long) this.mValue);
+ break;
+ case FLOAT:
+ out.write(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
+ (float) this.mValue);
+ break;
+ case DOUBLE:
+ out.write(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
+ (double) this.mValue);
+ break;
+ case CHAR:
+ out.write(RemoteViewsProto.ReflectionAction.CHAR_VALUE,
+ (Character) this.mValue);
+ break;
+ case STRING:
+ out.write(RemoteViewsProto.ReflectionAction.STRING_VALUE,
+ (String) this.mValue);
+ break;
+ case CHAR_SEQUENCE:
+ long csToken = out.start(
+ RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE);
+ RemoteViewsSerializers.writeCharSequenceToProto(out,
+ (CharSequence) this.mValue);
+ out.end(csToken);
+ break;
+ case URI:
+ out.write(RemoteViewsProto.ReflectionAction.URI_VALUE,
+ ((Uri) this.mValue).toString());
+ break;
+ case BITMAP:
+ final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ ((Bitmap) this.mValue).compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100,
+ bytes);
+ out.write(RemoteViewsProto.ReflectionAction.BITMAP_VALUE,
+ bytes.toByteArray());
+ break;
+ case BLEND_MODE:
+ out.write(RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE,
+ BlendMode.toValue((BlendMode) this.mValue));
+ break;
+ case COLOR_STATE_LIST:
+ writeColorStateListToProto(out, (ColorStateList) this.mValue,
+ RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE);
+ break;
+ case ICON:
+ writeIconToProto(out, appResources, (Icon) this.mValue,
+ RemoteViewsProto.ReflectionAction.ICON_VALUE);
+ break;
+ case BUNDLE:
+ case INTENT:
+ default:
+ break;
+ }
+ }
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.REFLECTION_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.ReflectionAction.VIEW_ID:
+ values.put(RemoteViewsProto.ReflectionAction.VIEW_ID,
+ in.readString(RemoteViewsProto.ReflectionAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.METHOD_NAME:
+ values.put(RemoteViewsProto.ReflectionAction.METHOD_NAME,
+ in.readString(RemoteViewsProto.ReflectionAction.METHOD_NAME));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.PARAMETER_TYPE:
+ values.put(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE,
+ in.readInt(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE,
+ in.readBoolean(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.BYTE_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.BYTE_VALUE,
+ in.readBytes(RemoteViewsProto.ReflectionAction.BYTE_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.SHORT_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
+ (short) in.readInt(RemoteViewsProto.ReflectionAction.SHORT_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.INT_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.INT_VALUE,
+ in.readInt(RemoteViewsProto.ReflectionAction.INT_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.LONG_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.LONG_VALUE,
+ in.readLong(RemoteViewsProto.ReflectionAction.LONG_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.FLOAT_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
+ in.readFloat(RemoteViewsProto.ReflectionAction.FLOAT_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.DOUBLE_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
+ in.readDouble(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.CHAR_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.CHAR_VALUE,
+ (char) in.readInt(RemoteViewsProto.ReflectionAction.CHAR_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.STRING_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.STRING_VALUE,
+ in.readString(RemoteViewsProto.ReflectionAction.STRING_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE,
+ createCharSequenceFromProto(in,
+ RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.URI_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.URI_VALUE,
+ in.readString(RemoteViewsProto.ReflectionAction.URI_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.BITMAP_VALUE:
+ byte[] bitmapData = in.readBytes(
+ RemoteViewsProto.ReflectionAction.BITMAP_VALUE);
+ values.put(RemoteViewsProto.ReflectionAction.BITMAP_VALUE,
+ BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE,
+ createColorStateListFromProto(in,
+ RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.ICON_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.ICON_VALUE,
+ createIconFromProto(in,
+ RemoteViewsProto.ReflectionAction.ICON_VALUE));
+ break;
+ case (int) RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE:
+ values.put(RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE,
+ BlendMode.fromValue(in.readInt(
+ RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE)));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.ReflectionAction.VIEW_ID,
+ RemoteViewsProto.ReflectionAction.METHOD_NAME,
+ RemoteViewsProto.ReflectionAction.PARAMETER_TYPE});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.ReflectionAction.VIEW_ID);
+ Object value = null;
+ int parameterType = (int) values.get(
+ RemoteViewsProto.ReflectionAction.PARAMETER_TYPE);
+ switch (parameterType) {
+ case BOOLEAN:
+ value = (boolean) values.get(
+ RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE, false);
+ break;
+ case BYTE:
+ byte[] bytes = (byte[]) values.get(
+ RemoteViewsProto.ReflectionAction.BYTE_VALUE);
+ if (bytes != null && bytes.length > 0) {
+ value = bytes[0];
+ }
+ break;
+ case SHORT:
+ value = (short) values.get(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
+ 0);
+ break;
+ case INT:
+ value = (int) values.get(RemoteViewsProto.ReflectionAction.INT_VALUE, 0);
+ break;
+ case LONG:
+ value = (long) values.get(RemoteViewsProto.ReflectionAction.LONG_VALUE, 0);
+ break;
+ case FLOAT:
+ value = (float) values.get(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
+ 0);
+ break;
+ case DOUBLE:
+ value = (double) values.get(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
+ 0);
+ break;
+ case CHAR:
+ value = (char) values.get(RemoteViewsProto.ReflectionAction.CHAR_VALUE, 0);
+ break;
+ case STRING:
+ value = (String) values.get(RemoteViewsProto.ReflectionAction.STRING_VALUE);
+ break;
+ case CHAR_SEQUENCE:
+ value = (CharSequence) values.get(
+ RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE);
+ break;
+ case URI:
+ value = Uri.parse(
+ (String) values.get(RemoteViewsProto.ReflectionAction.URI_VALUE));
+ break;
+ case BITMAP:
+ value = (Bitmap) values.get(RemoteViewsProto.ReflectionAction.BITMAP_VALUE);
+ break;
+ case BLEND_MODE:
+ value = (BlendMode) values.get(
+ RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE);
+ break;
+ case COLOR_STATE_LIST:
+ value = (ColorStateList) values.get(
+ RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE);
+ break;
+ case ICON:
+ value = ((PendingResources<Icon>) values.get(
+ RemoteViewsProto.ReflectionAction.ICON_VALUE)).create(context,
+ resources, rootData, depth);
+ break;
+ case BUNDLE:
+ case INTENT:
+ default:
+ // omit the action for unsupported parameter types
+ return null;
+ }
+ return new ReflectionAction(viewId,
+ (String) values.get(RemoteViewsProto.ReflectionAction.METHOD_NAME),
+ parameterType, value);
+ };
+ }
}
private static final class ResourceReflectionAction extends BaseReflectionAction {
@@ -2735,7 +3080,87 @@
public int getActionTag() {
return ATTRIBUTE_REFLECTION_ACTION_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION);
+ out.write(RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME, mMethodName);
+ out.write(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE, mType);
+ out.write(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE, mResourceType);
+ if (mAttrId != 0) {
+ out.write(RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID,
+ appResources.getResourceName(mAttrId));
+ }
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.AttributeReflectionAction.VIEW_ID: {
+ values.put(RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
+ in.readString(RemoteViewsProto.AttributeReflectionAction.VIEW_ID));
+ break;
+ }
+ case (int) RemoteViewsProto.AttributeReflectionAction.METHOD_NAME:
+ values.put(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME,
+ in.readString(
+ RemoteViewsProto.AttributeReflectionAction.METHOD_NAME));
+ break;
+ case (int) RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID:
+ values.put(RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID,
+ in.readString(
+ RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID));
+ break;
+ case (int) RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE:
+ values.put(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE,
+ in.readInt(
+ RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE));
+ break;
+ case (int) RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE:
+ values.put(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE,
+ in.readInt(
+ RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
+ RemoteViewsProto.AttributeReflectionAction.METHOD_NAME,
+ RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE,
+ RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.AttributeReflectionAction.VIEW_ID);
+ int attributeId = (values.indexOfKey(
+ RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID) >= 0)
+ ? getAsIdentifier(resources, values,
+ RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID) : 0;
+ return new AttributeReflectionAction(viewId,
+ (String) values.get(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME),
+ (int) values.get(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE),
+ (int) values.get(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE),
+ attributeId);
+ };
+ }
}
+
private static final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
private final float mValue;
@ComplexDimensionUnit
@@ -2789,6 +3214,101 @@
public int getActionTag() {
return COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(
+ RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION);
+ out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
+ mMethodName);
+ out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE, mType);
+ out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
+ mValue);
+ out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT, mUnit);
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(
+ RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID:
+ values.put(RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
+ in.readString(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME:
+ values.put(
+ RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
+ in.readString(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction.METHOD_NAME));
+ break;
+ case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE:
+ values.put(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction.PARAMETER_TYPE,
+ in.readInt(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction
+ .PARAMETER_TYPE));
+ break;
+ case (int) RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE:
+ values.put(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
+ in.readFloat(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction
+ .DIMENSION_VALUE));
+ break;
+ case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT:
+ values.put(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT,
+ in.readInt(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction.UNIT));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values,
+ new long[]{RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
+ RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
+ RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID);
+ return new ComplexUnitDimensionReflectionAction(viewId, (String) values.get(
+ RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME),
+ (int) values.get(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction.PARAMETER_TYPE),
+ (float) values.get(
+ RemoteViewsProto
+ .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
+ 0),
+ (int) values.get(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT,
+ 0));
+ };
+ }
}
private static final class NightModeReflectionAction extends BaseReflectionAction {
@@ -2863,6 +3383,145 @@
visitIconUri((Icon) mLightValue, visitor);
}
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION);
+ out.write(RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.NightModeReflectionAction.METHOD_NAME, mMethodName);
+ out.write(RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE, mType);
+ switch (this.mType) {
+ case ICON:
+ writeIconToProto(out, appResources, (Icon) mLightValue,
+ RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON);
+ writeIconToProto(out, appResources, (Icon) mDarkValue,
+ RemoteViewsProto.NightModeReflectionAction.DARK_ICON);
+ break;
+ case COLOR_STATE_LIST:
+ writeColorStateListToProto(out, (ColorStateList) mLightValue,
+ RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST);
+ writeColorStateListToProto(out, (ColorStateList) mDarkValue,
+ RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST);
+ break;
+ case INT:
+ out.write(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT,
+ (int) mLightValue);
+ out.write(RemoteViewsProto.NightModeReflectionAction.DARK_INT,
+ (int) mDarkValue);
+ break;
+ }
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.NightModeReflectionAction.VIEW_ID:
+ values.put(RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
+ in.readString(RemoteViewsProto.NightModeReflectionAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.NightModeReflectionAction.METHOD_NAME:
+ values.put(RemoteViewsProto.NightModeReflectionAction.METHOD_NAME,
+ in.readString(
+ RemoteViewsProto.NightModeReflectionAction.METHOD_NAME));
+ break;
+ case (int) RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE:
+ values.put(RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE,
+ in.readInt(
+ RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE));
+ break;
+ case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON:
+ values.put(RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON,
+ createIconFromProto(in,
+ RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON));
+ break;
+ case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST:
+ values.put(
+ RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST,
+ createColorStateListFromProto(in,
+ RemoteViewsProto
+ .NightModeReflectionAction.LIGHT_COLOR_STATE_LIST));
+ break;
+ case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_INT:
+ values.put(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT,
+ in.readInt(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT));
+ break;
+ case (int) RemoteViewsProto.NightModeReflectionAction.DARK_ICON:
+ values.put(RemoteViewsProto.NightModeReflectionAction.DARK_ICON,
+ createIconFromProto(in,
+ RemoteViewsProto.NightModeReflectionAction.DARK_ICON));
+ break;
+ case (int) RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST:
+ values.put(RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST,
+ createColorStateListFromProto(in,
+ RemoteViewsProto
+ .NightModeReflectionAction.DARK_COLOR_STATE_LIST));
+ break;
+ case (int) RemoteViewsProto.NightModeReflectionAction.DARK_INT:
+ values.put(RemoteViewsProto.NightModeReflectionAction.DARK_INT,
+ in.readInt(RemoteViewsProto.NightModeReflectionAction.DARK_INT));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
+ RemoteViewsProto.NightModeReflectionAction.METHOD_NAME,
+ RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.NightModeReflectionAction.VIEW_ID);
+ String methodName = (String) values.get(
+ RemoteViewsProto.NightModeReflectionAction.METHOD_NAME);
+ int parameterType = (int) values.get(
+ RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE);
+ switch (parameterType) {
+ case ICON:
+ PendingResources<Icon> pendingLightIcon =
+ (PendingResources<Icon>) values.get(
+ RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON);
+ PendingResources<Icon> pendingDarkIcon =
+ (PendingResources<Icon>) values.get(
+ RemoteViewsProto.NightModeReflectionAction.DARK_ICON);
+ Icon lightIcon = pendingLightIcon != null ? pendingLightIcon.create(context,
+ resources, rootData, depth) : null;
+ Icon darkIcon = pendingDarkIcon != null ? pendingDarkIcon.create(context,
+ resources, rootData, depth) : null;
+ return new NightModeReflectionAction(viewId, methodName, parameterType,
+ lightIcon, darkIcon);
+ case COLOR_STATE_LIST:
+ return new NightModeReflectionAction(viewId, methodName, parameterType,
+ (ColorStateList) values.get(
+ RemoteViewsProto
+ .NightModeReflectionAction.LIGHT_COLOR_STATE_LIST),
+ (ColorStateList) values.get(
+ RemoteViewsProto
+ .NightModeReflectionAction.DARK_COLOR_STATE_LIST));
+ case INT:
+ return new NightModeReflectionAction(viewId, methodName, parameterType,
+ (int) values.get(
+ RemoteViewsProto.NightModeReflectionAction.LIGHT_INT, 0),
+ (int) values.get(
+ RemoteViewsProto.NightModeReflectionAction.DARK_INT, 0));
+ default:
+ throw new RuntimeException("Unknown parameterType: " + parameterType);
+ }
+ };
+ }
}
/**
@@ -3348,6 +4007,46 @@
public int mergeBehavior() {
return MERGE_APPEND;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION);
+ out.write(RemoteViewsProto.RemoveFromParentAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.RemoveFromParentAction.VIEW_ID:
+ values.put(RemoteViewsProto.RemoveFromParentAction.VIEW_ID,
+ in.readString(RemoteViewsProto.RemoveFromParentAction.VIEW_ID));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.RemoveFromParentAction.VIEW_ID});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.RemoveFromParentAction.VIEW_ID);
+ return new RemoveFromParentAction(viewId);
+ };
+ }
}
/**
@@ -3763,6 +4462,64 @@
public String getUniqueKey() {
return super.getUniqueKey() + mProperty;
}
+
+ @Override
+ public boolean canWriteToProto() {
+ return true;
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+ final long token = out.start(RemoteViewsProto.Action.LAYOUT_PARAM_ACTION);
+ out.write(RemoteViewsProto.LayoutParamAction.VIEW_ID,
+ appResources.getResourceName(mViewId));
+ out.write(RemoteViewsProto.LayoutParamAction.PROPERTY, mProperty);
+ out.write(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE, mValue);
+ out.write(RemoteViewsProto.LayoutParamAction.VALUE_TYPE, mValueType);
+ out.end(token);
+ }
+
+ public static PendingResources<Action> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+
+ final long token = in.start(RemoteViewsProto.Action.LAYOUT_PARAM_ACTION);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.LayoutParamAction.VIEW_ID:
+ values.put(RemoteViewsProto.LayoutParamAction.VIEW_ID,
+ in.readString(RemoteViewsProto.LayoutParamAction.VIEW_ID));
+ break;
+ case (int) RemoteViewsProto.LayoutParamAction.PROPERTY:
+ values.put(RemoteViewsProto.LayoutParamAction.PROPERTY,
+ in.readInt(RemoteViewsProto.LayoutParamAction.PROPERTY));
+ break;
+ case (int) RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE:
+ values.put(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE,
+ in.readInt(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE));
+ break;
+ case (int) RemoteViewsProto.LayoutParamAction.VALUE_TYPE:
+ values.put(RemoteViewsProto.LayoutParamAction.VALUE_TYPE,
+ in.readInt(RemoteViewsProto.LayoutParamAction.VALUE_TYPE));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ checkContainsKeys(values, new long[]{RemoteViewsProto.LayoutParamAction.VIEW_ID});
+
+ return (context, resources, rootData, depth) -> {
+ int viewId = getAsIdentifier(resources, values,
+ RemoteViewsProto.LayoutParamAction.VIEW_ID);
+ return new LayoutParamAction(viewId,
+ (int) values.get(RemoteViewsProto.LayoutParamAction.PROPERTY, 0),
+ (int) values.get(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE, 0),
+ (int) values.get(RemoteViewsProto.LayoutParamAction.VALUE_TYPE, 0));
+ };
+ }
}
/**
@@ -7663,6 +8420,7 @@
values.put(RemoteViewsProto.RemoteCollectionItems.IDS, new ArrayList<Long>());
values.put(RemoteViewsProto.RemoteCollectionItems.VIEWS,
new ArrayList<PendingResources<RemoteViews>>());
+
while (in.nextField() != NO_MORE_FIELDS) {
switch (in.getFieldNumber()) {
case (int) RemoteViewsProto.RemoteCollectionItems.IDS:
@@ -7699,6 +8457,7 @@
checkContainsKeys(values,
new long[]{RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT});
+
return (context, resources, rootData, depth) -> {
List<Long> idList = (List<Long>) values.get(
RemoteViewsProto.RemoteCollectionItems.IDS);
@@ -8144,6 +8903,16 @@
out.write(SizeFProto.HEIGHT, mIdealSize.getHeight());
out.end(token);
}
+
+ if (mActions != null) {
+ for (Action action : mActions) {
+ if (action.canWriteToProto()) {
+ final long token = out.start(RemoteViewsProto.ACTIONS);
+ action.writeToProto(out, context, appResources);
+ out.end(token);
+ }
+ }
+ }
} else if (hasSizedRemoteViews()) {
out.write(RemoteViewsProto.MODE, MODE_HAS_SIZED_REMOTEVIEWS);
for (RemoteViews view : mSizedRemoteViews) {
@@ -8187,6 +8956,7 @@
String mLayoutResName = null;
String mLightBackgroundResName = null;
String mViewResName = null;
+ final List<PendingResources<Action>> mActions = new ArrayList<>();
final List<PendingResources<RemoteViews>> mSizedRemoteViews = new ArrayList<>();
PendingResources<RemoteViews> mLandscapeViews = null;
PendingResources<RemoteViews> mPortraitViews = null;
@@ -8225,6 +8995,14 @@
case (int) RemoteViewsProto.PROVIDER_INSTANCE_ID:
ref.mProviderInstanceId = in.readInt(RemoteViewsProto.PROVIDER_INSTANCE_ID);
break;
+ case (int) RemoteViewsProto.ACTIONS:
+ final long actionsToken = in.start(RemoteViewsProto.ACTIONS);
+ final PendingResources<Action> action = createActionFromProto(ref.mRv, in);
+ if (action != null) {
+ ref.mActions.add(action);
+ }
+ in.end(actionsToken);
+ break;
case (int) RemoteViewsProto.SIZED_REMOTEVIEWS:
final long sizedToken = in.start(RemoteViewsProto.SIZED_REMOTEVIEWS);
ref.mSizedRemoteViews.add(createFromProto(in));
@@ -8323,19 +9101,27 @@
}
}
if (ref.mPopulateRemoteCollectionCache != null) {
- ref.mPopulateRemoteCollectionCache.create(context, resources, rootData, depth);
+ ref.mPopulateRemoteCollectionCache.create(appContext, appResources, rootData,
+ depth);
}
if (ref.mProviderInstanceId != -1) {
rv.mProviderInstanceId = ref.mProviderInstanceId;
}
if (ref.mMode == MODE_NORMAL) {
rv.setIdealSize(ref.mIdealSize);
+ for (PendingResources<Action> pendingAction : ref.mActions) {
+ Action action = pendingAction.create(appContext, appResources, rootData, depth);
+ if (action != null) {
+ rv.addAction(action);
+ }
+ }
return rv;
} else if (ref.mMode == MODE_HAS_SIZED_REMOTEVIEWS) {
List<RemoteViews> sizedViews = new ArrayList<>();
for (RemoteViews.PendingResources<RemoteViews> pendingViews :
ref.mSizedRemoteViews) {
- RemoteViews views = pendingViews.create(context, resources, rootData, depth);
+ RemoteViews views = pendingViews.create(appContext, appResources, rootData,
+ depth);
sizedViews.add(views);
}
rv.initializeSizedRemoteViews(sizedViews.iterator());
@@ -8344,8 +9130,8 @@
checkProtoResultNotNull(ref.mLandscapeViews, "Missing landscape views");
checkProtoResultNotNull(ref.mPortraitViews, "Missing portrait views");
RemoteViews parentRv = new RemoteViews(
- ref.mLandscapeViews.create(context, resources, rootData, depth),
- ref.mPortraitViews.create(context, resources, rootData, depth));
+ ref.mLandscapeViews.create(appContext, appResources, rootData, depth),
+ ref.mPortraitViews.create(appContext, appResources, rootData, depth));
parentRv.initializeFrom(/* src= */ rv, /* hierarchyRoot= */ rv);
return parentRv;
} else {
@@ -8365,6 +9151,35 @@
throws Exception;
}
+ @Nullable
+ private static PendingResources<Action> createActionFromProto(RemoteViews rv,
+ ProtoInputStream in) throws Exception {
+ int actionFieldId = in.nextField();
+ if (actionFieldId == NO_MORE_FIELDS) {
+ // action was omitted
+ return null;
+ }
+ switch (actionFieldId) {
+ case (int) RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION:
+ return AttributeReflectionAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION:
+ return rv.createFromBitmapReflectionActionFromProto(in);
+ case (int) RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION:
+ return ComplexUnitDimensionReflectionAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.LAYOUT_PARAM_ACTION:
+ return LayoutParamAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION:
+ return NightModeReflectionAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.REFLECTION_ACTION:
+ return ReflectionAction.createFromProto(in);
+ case (int) RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION:
+ return RemoveFromParentAction.createFromProto(in);
+ default:
+ throw new RuntimeException("Unhandled field while reading Action proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+
private static void checkValidResource(int id, String message, String resName)
throws Exception {
if (id == 0) throw new Exception(message + ": " + resName);
@@ -8387,6 +9202,22 @@
}
}
+ private static int getAsIdentifier(Resources resources, LongSparseArray<?> array, long fieldId)
+ throws Exception {
+ String resName = (String) array.get(fieldId);
+ int id = resources.getIdentifier(resName, /* defType= */ null, /* defPackage= */ null);
+ checkValidResource(id, "Invalid id", resName);
+ return id;
+ }
+
+ private static int getAsIdentifier(Resources resources, SparseArray<?> array, int key)
+ throws Exception {
+ String resName = (String) array.get(key);
+ int id = resources.getIdentifier(resName, /* defType= */ null, /* defPackage= */ null);
+ checkValidResource(id, "Invalid id", resName);
+ return id;
+ }
+
private static SizeF createSizeFFromProto(ProtoInputStream in) throws Exception {
float width = 0;
float height = 0;
@@ -8406,4 +9237,43 @@
return new SizeF(width, height);
}
+
+ private static void writeIconToProto(ProtoOutputStream out, Resources appResources, Icon icon,
+ long fieldId) {
+ long token = out.start(fieldId);
+ RemoteViewsSerializers.writeIconToProto(out, appResources, icon);
+ out.end(token);
+ }
+
+ private static PendingResources<Icon> createIconFromProto(ProtoInputStream in, long fieldId)
+ throws Exception {
+ long token = in.start(fieldId);
+ Function<Resources, Icon> icon = RemoteViewsSerializers.createIconFromProto(in);
+ in.end(token);
+ return (context, resources, rootData, depth) -> icon.apply(resources);
+ }
+
+ private static void writeColorStateListToProto(ProtoOutputStream out,
+ ColorStateList colorStateList, long fieldId) {
+ long token = out.start(fieldId);
+ colorStateList.writeToProto(out);
+ out.end(token);
+ }
+
+ private static ColorStateList createColorStateListFromProto(ProtoInputStream in, long fieldId)
+ throws Exception {
+ long token = in.start(fieldId);
+ ColorStateList colorStateList = ColorStateList.createFromProto(in);
+ in.end(token);
+ return colorStateList;
+ }
+
+ private static CharSequence createCharSequenceFromProto(ProtoInputStream in, long fieldId)
+ throws Exception {
+ long token = in.start(fieldId);
+ CharSequence cs = RemoteViewsSerializers.createCharSequenceFromProto(in);
+ in.end(token);
+ return cs;
+ }
+
}
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 2f28a87..118edc2 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -241,6 +241,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ enqueueDeferredUnbindServiceMessage();
+ }
+
+ @Override
public void handleMessage(Message msg) {
RemoteViewsAdapter adapter = mAdapter.get();
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index b5bf529..511c832 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.view.flags.Flags.enableTouchScrollFeedback;
import static android.view.flags.Flags.viewVelocityApi;
import android.annotation.ColorInt;
@@ -846,6 +847,8 @@
deltaY += mTouchSlop;
}
}
+ boolean hitTopLimit = false;
+ boolean hitBottomLimit = false;
if (mIsBeingDragged) {
// Scroll to follow the motion event
mLastMotionY = y - mScrollOffset[1];
@@ -889,12 +892,14 @@
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
+ hitTopLimit = true;
} else if (pulledToY > range) {
mEdgeGlowBottom.onPullDistance((float) deltaY / getHeight(),
1.f - displacement);
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
+ hitBottomLimit = true;
}
if (shouldDisplayEdgeEffects()
&& (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
@@ -902,6 +907,20 @@
}
}
}
+
+ // TODO: b/360198915 - Add unit tests.
+ if (enableTouchScrollFeedback()) {
+ if (hitTopLimit || hitBottomLimit) {
+ initHapticScrollFeedbackProviderIfNotExists();
+ mHapticScrollFeedbackProvider.onScrollLimit(vtev.getDeviceId(),
+ vtev.getSource(), MotionEvent.AXIS_Y,
+ /* isStart= */ hitTopLimit);
+ } else if (Math.abs(deltaY) != 0) {
+ initHapticScrollFeedbackProviderIfNotExists();
+ mHapticScrollFeedbackProvider.onScrollProgress(vtev.getDeviceId(),
+ vtev.getSource(), MotionEvent.AXIS_Y, deltaY);
+ }
+ }
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a346a67..ef941da 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1659,11 +1659,7 @@
}
if (!hasUseBoundForWidthValue) {
- if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
- mUseBoundsForWidth = Flags.useBoundsForWidth();
- } else {
- mUseBoundsForWidth = false;
- }
+ mUseBoundsForWidth = CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH);
}
// TODO(b/179693024): Use a ChangeId instead.
@@ -14375,7 +14371,7 @@
Matrix matrix = mTempMatrix;
matrix.reset();
- transformMatrixToLocal(matrix);
+ transformMatrixRootToLocal(matrix);
editorBounds.set(rect);
// When the view has transformations like scaleX/scaleY computing the global visible
// rectangle will already apply the transformations. The getLocalVisibleRect only offsets
diff --git a/core/java/android/widget/flags/flags.aconfig b/core/java/android/widget/flags/flags.aconfig
new file mode 100644
index 0000000..f0ed83b
--- /dev/null
+++ b/core/java/android/widget/flags/flags.aconfig
@@ -0,0 +1,11 @@
+package: "android.widget.flags"
+container: "system"
+flag {
+ name: "enable_fading_view_group"
+ namespace: "system_performance"
+ description: "FRP screen during OOBE must have fading and scaling animation in Wear Watches"
+ bug: "348515581"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/window/BackAnimationAdapter.java b/core/java/android/window/BackAnimationAdapter.java
index 5eb34e6..153e153 100644
--- a/core/java/android/window/BackAnimationAdapter.java
+++ b/core/java/android/window/BackAnimationAdapter.java
@@ -16,9 +16,12 @@
package android.window;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+
/**
* Object that describes how to run a remote back animation.
*
@@ -26,6 +29,7 @@
*/
public class BackAnimationAdapter implements Parcelable {
private final IBackAnimationRunner mRunner;
+ private int[] mSupportedAnimators;
public BackAnimationAdapter(IBackAnimationRunner runner) {
mRunner = runner;
@@ -33,12 +37,23 @@
public BackAnimationAdapter(Parcel in) {
mRunner = IBackAnimationRunner.Stub.asInterface(in.readStrongBinder());
+ mSupportedAnimators = new int[in.readInt()];
+ in.readIntArray(mSupportedAnimators);
}
public IBackAnimationRunner getRunner() {
return mRunner;
}
+ /** Update the latest animators in the system. */
+ public void updateSupportedAnimators(@NonNull ArrayList<Integer> animators) {
+ final int size = animators.size();
+ mSupportedAnimators = new int[size];
+ for (int i = size - 1; i >= 0; --i) {
+ mSupportedAnimators[i] = animators.get(i);
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -47,6 +62,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongInterface(mRunner);
+ dest.writeInt(mSupportedAnimators.length);
+ dest.writeIntArray(mSupportedAnimators);
}
public static final @android.annotation.NonNull Creator<BackAnimationAdapter> CREATOR =
@@ -59,4 +76,19 @@
return new BackAnimationAdapter[size];
}
};
+
+ /**
+ * Check if the back type is animatable.
+ */
+ public boolean isAnimatable(@BackNavigationInfo.BackTargetType int backType) {
+ if (mSupportedAnimators == null) {
+ return false;
+ }
+ for (int i = mSupportedAnimators.length - 1; i >= 0; --i) {
+ if (backType == mSupportedAnimators[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index 12d4ab8..465e11a 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -212,6 +212,17 @@
mBackCancelledFinishRunnable = null;
}
+ /**
+ * Removes the finishCallback passed into {@link #onBackCancelled}
+ */
+ public void removeOnBackInvokedFinishCallback() {
+ if (mBackInvokedFlingAnim != null) {
+ mBackInvokedFlingAnim.removeUpdateListener(mOnBackInvokedFlingUpdateListener);
+ mBackInvokedFlingAnim.removeEndListener(mOnAnimationEndListener);
+ }
+ mBackInvokedFinishRunnable = null;
+ }
+
/** Returns true if the back animation is in progress. */
@VisibleForTesting(visibility = PACKAGE)
public boolean isBackAnimationInProgress() {
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 58b5757..b8a11cf0 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -47,6 +47,19 @@
void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
/**
+ * Registers remote animations per transition type for the organizer. It will override the
+ * animations if the transition only contains windows that belong to the organized
+ * TaskFragments in the given Task.
+ */
+ void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
+ in RemoteAnimationDefinition definition);
+
+ /**
+ * Unregisters remote animations per transition type for the organizer.
+ */
+ void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
+
+ /**
* Saves the state in the system, where the state can be restored if the process of
* the TaskFragmentOrganizer is restarted.
*/
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 2f595d1..9a7bce0 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -151,9 +151,11 @@
@VisibleForTesting
public void setFrames(Rect frame, Rect systemBarInsets) {
mFrame.set(frame);
- mSystemBarInsets.set(systemBarInsets);
mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH);
- mSystemBarBackgroundPainter.setInsets(systemBarInsets);
+ if (!Flags.drawSnapshotAspectRatioMatch() && systemBarInsets != null) {
+ mSystemBarInsets.set(systemBarInsets);
+ mSystemBarBackgroundPainter.setInsets(systemBarInsets);
+ }
}
private void drawSnapshot(boolean releaseAfterDraw) {
@@ -394,9 +396,12 @@
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
final ActivityManager.TaskDescription taskDescription =
getOrCreateTaskDescription(runningTaskInfo);
- drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
- attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
- final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
+ Rect systemBarInsets = null;
+ if (!Flags.drawSnapshotAspectRatioMatch()) {
+ drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
+ attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
+ systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
+ }
drawSurface.setFrames(windowBounds, systemBarInsets);
drawSurface.drawSnapshot(releaseAfterDraw);
}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 027d323..c3168001 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -34,6 +34,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import com.android.window.flags.Flags;
@@ -68,6 +69,23 @@
public static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type";
/**
+ * Key to bundle {@link TaskFragmentInfo}s from the system in
+ * {@link #registerOrganizer(boolean, Bundle)}
+ *
+ * @hide
+ */
+ public static final String KEY_RESTORE_TASK_FRAGMENTS_INFO = "key_restore_task_fragments_info";
+
+ /**
+ * Key to bundle {@link TaskFragmentParentInfo} from the system in
+ * {@link #registerOrganizer(boolean, Bundle)}
+ *
+ * @hide
+ */
+ public static final String KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO =
+ "key_restore_task_fragment_parent_info";
+
+ /**
* No change set.
*/
@WindowManager.TransitionType
@@ -225,6 +243,34 @@
}
/**
+ * Registers remote animations per transition type for the organizer. It will override the
+ * animations if the transition only contains windows that belong to the organized
+ * TaskFragments, and at least one of the transition window is embedded (not filling the Task).
+ * @hide
+ */
+ @CallSuper
+ public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
+ try {
+ getController().registerRemoteAnimations(mInterface, definition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters remote animations per transition type for the organizer.
+ * @hide
+ */
+ @CallSuper
+ public void unregisterRemoteAnimations() {
+ try {
+ getController().unregisterRemoteAnimations(mInterface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Saves the state in the system, where the state can be restored if the process of
* the TaskFragmentOrganizer is restarted.
*
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index 1555416..efd74c3 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -41,6 +41,8 @@
private final int mDisplayId;
+ private final int mTaskId;
+
private final boolean mVisible;
private final boolean mHasDirectActivity;
@@ -49,9 +51,11 @@
/** @hide */
public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
- boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) {
+ int taskId, boolean visible, boolean hasDirectActivity,
+ @Nullable SurfaceControl decorSurface) {
mConfiguration.setTo(configuration);
mDisplayId = displayId;
+ mTaskId = taskId;
mVisible = visible;
mHasDirectActivity = hasDirectActivity;
mDecorSurface = decorSurface;
@@ -61,6 +65,7 @@
public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
mConfiguration.setTo(info.getConfiguration());
mDisplayId = info.mDisplayId;
+ mTaskId = info.mTaskId;
mVisible = info.mVisible;
mHasDirectActivity = info.mHasDirectActivity;
mDecorSurface = info.mDecorSurface;
@@ -87,6 +92,15 @@
}
/**
+ * The id of the parent Task.
+ *
+ * @hide
+ */
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ /**
* Whether the parent Task is visible or not
*
* @hide
@@ -120,7 +134,8 @@
return false;
}
return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
- && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity
+ && mTaskId == that.mTaskId && mVisible == that.mVisible
+ && mHasDirectActivity == that.mHasDirectActivity
&& mDecorSurface == that.mDecorSurface;
}
@@ -140,6 +155,7 @@
return TaskFragmentParentInfo.class.getSimpleName() + ":{"
+ "config=" + mConfiguration
+ ", displayId=" + mDisplayId
+ + ", taskId=" + mTaskId
+ ", visible=" + mVisible
+ ", hasDirectActivity=" + mHasDirectActivity
+ ", decorSurface=" + mDecorSurface
@@ -163,6 +179,7 @@
final TaskFragmentParentInfo that = (TaskFragmentParentInfo) obj;
return mConfiguration.equals(that.mConfiguration)
&& mDisplayId == that.mDisplayId
+ && mTaskId == that.mTaskId
&& mVisible == that.mVisible
&& mHasDirectActivity == that.mHasDirectActivity
&& mDecorSurface == that.mDecorSurface;
@@ -172,6 +189,7 @@
public int hashCode() {
int result = mConfiguration.hashCode();
result = 31 * result + mDisplayId;
+ result = 31 * result + mTaskId;
result = 31 * result + (mVisible ? 1 : 0);
result = 31 * result + (mHasDirectActivity ? 1 : 0);
result = 31 * result + Objects.hashCode(mDecorSurface);
@@ -183,6 +201,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
mConfiguration.writeToParcel(dest, flags);
dest.writeInt(mDisplayId);
+ dest.writeInt(mTaskId);
dest.writeBoolean(mVisible);
dest.writeBoolean(mHasDirectActivity);
dest.writeTypedObject(mDecorSurface, flags);
@@ -191,6 +210,7 @@
private TaskFragmentParentInfo(Parcel in) {
mConfiguration.readFromParcel(in);
mDisplayId = in.readInt();
+ mTaskId = in.readInt();
mVisible = in.readBoolean();
mHasDirectActivity = in.readBoolean();
mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR);
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/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index b6c0d7c..bb89a24 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -243,6 +243,8 @@
if (previousTopCallback == callback) {
// We should call onBackCancelled() when an active callback is removed from
// dispatcher.
+ mProgressAnimator.removeOnBackCancelledFinishCallback();
+ mProgressAnimator.removeOnBackInvokedFinishCallback();
sendCancelledIfInProgress(callback);
mHandler.post(mProgressAnimator::reset);
setTopOnBackInvokedCallback(getTopCallback());
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index ebf87f1..8e81951 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"
@@ -31,6 +23,13 @@
}
flag {
+ name: "enable_windowing_scaled_resizing"
+ namespace: "lse_desktop_experience"
+ description: "Enables the resizing of non-resizable apps through scaling their bounds up/down"
+ bug: "319844447"
+}
+
+flag {
name: "disable_non_resizable_app_snap_resizing"
namespace: "lse_desktop_experience"
description: "Stops non-resizable app desktop windows from being snap resized"
@@ -245,3 +244,17 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_desktop_windowing_persistence"
+ namespace: "lse_desktop_experience"
+ description: "Persists the desktop windowing session across reboots."
+ bug: "350456942"
+}
+
+flag {
+ name: "enable_display_focus_in_shell_transitions"
+ namespace: "lse_desktop_experience"
+ description: "Creates a shell transition when display focus switches."
+ bug: "356109871"
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 6ce9725..cd31850 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -71,3 +71,11 @@
bug: "339720406"
}
+flag {
+ name: "bal_reduce_grace_period"
+ namespace: "responsible_apis"
+ description: "Changes to reduce or ideally remove the grace period exemption."
+ bug: "362575865"
+}
+
+
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
index 01cbb55..81d8adf 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
@@ -20,7 +20,6 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityButtonLongPressStatus;
@@ -44,6 +43,8 @@
* Activity used to display and persist a service or feature target for the Accessibility button.
*/
public class AccessibilityButtonChooserActivity extends Activity {
+ public static final String EXTRA_TYPE_TO_CHOOSE = "TYPE";
+
private final List<AccessibilityTarget> mTargets = new ArrayList<>();
@Override
@@ -67,8 +68,8 @@
NAV_BAR_MODE_GESTURAL == getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode);
- final int targetType = (isGestureNavigateEnabled
- && android.provider.Flags.a11yStandaloneGestureEnabled()) ? GESTURE : SOFTWARE;
+ final int targetType = android.provider.Flags.a11yStandaloneGestureEnabled()
+ ? getIntent().getIntExtra(EXTRA_TYPE_TO_CHOOSE, SOFTWARE) : SOFTWARE;
if (isGestureNavigateEnabled) {
final TextView promptPrologue = findViewById(R.id.accessibility_button_prompt_prologue);
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/app/SetScreenLockDialogActivity.java b/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
index 360fcaf..4c3af4d 100644
--- a/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
+++ b/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
@@ -60,6 +60,7 @@
LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS,
LAUNCH_REASON_DISABLE_QUIET_MODE,
LAUNCH_REASON_UNKNOWN,
+ LAUNCH_REASON_RESET_PRIVATE_SPACE_SETTINGS_ACCESS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface LaunchReason {
@@ -67,6 +68,7 @@
public static final int LAUNCH_REASON_UNKNOWN = -1;
public static final int LAUNCH_REASON_DISABLE_QUIET_MODE = 1;
public static final int LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS = 2;
+ public static final int LAUNCH_REASON_RESET_PRIVATE_SPACE_SETTINGS_ACCESS = 3;
private @LaunchReason int mReason;
private int mOriginUserId;
@@ -139,7 +141,11 @@
// Always set private space message if launch reason is specific to private space
builder.setMessage(R.string.private_space_set_up_screen_lock_message);
return;
+ } else if (mReason == LAUNCH_REASON_RESET_PRIVATE_SPACE_SETTINGS_ACCESS) {
+ builder.setMessage(R.string.private_space_set_up_screen_lock_for_reset);
+ return;
}
+
final UserManager userManager = getApplicationContext().getSystemService(UserManager.class);
if (userManager != null) {
UserInfo userInfo = userManager.getUserInfo(mOriginUserId);
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 9f5ed65..21fbf9d 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -134,7 +134,8 @@
* Prints data on dumpsys.
*/
public void dump(PrintWriter pw) {
- pw.println("BrightnessSynchronizer");
+ pw.println("BrightnessSynchronizer:");
+ pw.println("-----------------------");
pw.println(" mLatestIntBrightness=" + mLatestIntBrightness);
pw.println(" mLatestFloatBrightness=" + mLatestFloatBrightness);
pw.println(" mCurrentUpdate=" + mCurrentUpdate);
diff --git a/core/java/com/android/internal/jank/flags.aconfig b/core/java/com/android/internal/jank/flags.aconfig
index 676bb70..82f50ae 100644
--- a/core/java/com/android/internal/jank/flags.aconfig
+++ b/core/java/com/android/internal/jank/flags.aconfig
@@ -3,7 +3,8 @@
flag {
name: "use_sf_frame_duration"
- namespace: "android_platform_window_surfaces"
+ namespace: "window_surfaces"
description: "Whether to get the frame duration from SurfaceFlinger, or HWUI"
bug: "354763298"
+ is_fixed_read_only: true
}
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 07fa679..dfb2884 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -18,6 +18,10 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
import com.android.internal.util.Preconditions;
@@ -55,18 +59,15 @@
*
* @hide
*/
[email protected]
[email protected](
- "com.android.platform.test.ravenwood.nativesubstitution.LongArrayMultiStateCounter_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("LongArrayMultiStateCounter_host")
public final class LongArrayMultiStateCounter implements Parcelable {
/**
* Container for a native equivalent of a long[].
*/
- @android.ravenwood.annotation.RavenwoodKeepWholeClass
- @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution"
- + ".LongArrayMultiStateCounter_host$LongArrayContainer_host")
+ @RavenwoodKeepWholeClass
+ @RavenwoodRedirectionClass("LongArrayContainer_host")
public static class LongArrayContainer {
private static NativeAllocationRegistry sRegistry;
@@ -81,7 +82,7 @@
registerNativeAllocation();
}
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodReplace
private void registerNativeAllocation() {
if (sRegistry == null) {
synchronized (LongArrayMultiStateCounter.class) {
@@ -140,18 +141,23 @@
}
@CriticalNative
+ @RavenwoodRedirect
private static native long native_init(int length);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_getReleaseFunc();
@FastNative
+ @RavenwoodRedirect
private static native void native_setValues(long nativeObject, long[] array);
@FastNative
+ @RavenwoodRedirect
private static native void native_getValues(long nativeObject, long[] array);
@FastNative
+ @RavenwoodRedirect
private static native boolean native_combineValues(long nativeObject, long[] array,
int[] indexMap);
}
@@ -175,7 +181,7 @@
registerNativeAllocation();
}
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodReplace
private void registerNativeAllocation() {
if (sRegistry == null) {
synchronized (LongArrayMultiStateCounter.class) {
@@ -374,57 +380,73 @@
@CriticalNative
+ @RavenwoodRedirect
private static native long native_init(int stateCount, int arrayLength);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_getReleaseFunc();
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setEnabled(long nativeObject, boolean enabled,
long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setState(long nativeObject, int state, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_copyStatesFrom(long nativeObjectTarget,
long nativeObjectSource);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setValues(long nativeObject, int state,
long longArrayContainerNativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_updateValues(long nativeObject,
long longArrayContainerNativeObject, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_incrementValues(long nativeObject,
long longArrayContainerNativeObject, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_addCounts(long nativeObject,
long longArrayContainerNativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_reset(long nativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_getCounts(long nativeObject,
long longArrayContainerNativeObject, int state);
@FastNative
+ @RavenwoodRedirect
private static native String native_toString(long nativeObject);
@FastNative
+ @RavenwoodRedirect
private static native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
@FastNative
+ @RavenwoodRedirect
private static native long native_initFromParcel(Parcel parcel);
@CriticalNative
+ @RavenwoodRedirect
private static native int native_getStateCount(long nativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native int native_getArrayLength(long nativeObject);
}
diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java
index e5662c7..c386a86 100644
--- a/core/java/com/android/internal/os/LongMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongMultiStateCounter.java
@@ -18,6 +18,10 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
import com.android.internal.util.Preconditions;
@@ -55,9 +59,8 @@
*
* @hide
*/
[email protected]
[email protected](
- "com.android.platform.test.ravenwood.nativesubstitution.LongMultiStateCounter_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("LongMultiStateCounter_host")
public final class LongMultiStateCounter implements Parcelable {
private static NativeAllocationRegistry sRegistry;
@@ -82,7 +85,7 @@
mStateCount = native_getStateCount(mNativeObject);
}
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodReplace
private void registerNativeAllocation() {
if (sRegistry == null) {
synchronized (LongMultiStateCounter.class) {
@@ -210,43 +213,56 @@
@CriticalNative
+ @RavenwoodRedirect
private static native long native_init(int stateCount);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_getReleaseFunc();
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setEnabled(long nativeObject, boolean enabled,
long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setState(long nativeObject, int state, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_updateValue(long nativeObject, long value, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_incrementValue(long nativeObject, long increment,
long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_addCount(long nativeObject, long count);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_reset(long nativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_getCount(long nativeObject, int state);
@FastNative
+ @RavenwoodRedirect
private static native String native_toString(long nativeObject);
@FastNative
+ @RavenwoodRedirect
private static native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
@FastNative
+ @RavenwoodRedirect
private static native long native_initFromParcel(Parcel parcel);
@CriticalNative
+ @RavenwoodRedirect
private static native int native_getStateCount(long nativeObject);
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index cdac097..1709ca7 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -404,6 +404,17 @@
}
public static void redirectLogStreams$ravenwood() {
+ if (sOut$ravenwood != null && sErr$ravenwood != null) {
+ return; // Already initialized.
+ }
+
+ // Make sure the Log class is loaded and the JNI methods are hooked up,
+ // before redirecting System.out/err.
+ // Otherwise, because ClassLoadHook tries to write to System.out, this would cause
+ // a circular initialization problem and would cause a UnsatisfiedLinkError
+ // on the JNI methods.
+ Log.isLoggable("X", Log.VERBOSE);
+
if (sOut$ravenwood == null) {
sOut$ravenwood = System.out;
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b9cc457..2acda8a 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -631,21 +631,20 @@
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
- long capabilities = posixCapabilitiesAsBits(
- OsConstants.CAP_IPC_LOCK,
- OsConstants.CAP_KILL,
- OsConstants.CAP_NET_ADMIN,
- OsConstants.CAP_NET_BIND_SERVICE,
- OsConstants.CAP_NET_BROADCAST,
- OsConstants.CAP_NET_RAW,
- OsConstants.CAP_SYS_MODULE,
- OsConstants.CAP_SYS_NICE,
- OsConstants.CAP_SYS_PTRACE,
- OsConstants.CAP_SYS_TIME,
- OsConstants.CAP_SYS_TTY_CONFIG,
- OsConstants.CAP_WAKE_ALARM,
- OsConstants.CAP_BLOCK_SUSPEND
- );
+ long capabilities =
+ (1L << OsConstants.CAP_IPC_LOCK) |
+ (1L << OsConstants.CAP_KILL) |
+ (1L << OsConstants.CAP_NET_ADMIN) |
+ (1L << OsConstants.CAP_NET_BIND_SERVICE) |
+ (1L << OsConstants.CAP_NET_BROADCAST) |
+ (1L << OsConstants.CAP_NET_RAW) |
+ (1L << OsConstants.CAP_SYS_MODULE) |
+ (1L << OsConstants.CAP_SYS_NICE) |
+ (1L << OsConstants.CAP_SYS_PTRACE) |
+ (1L << OsConstants.CAP_SYS_TIME) |
+ (1L << OsConstants.CAP_SYS_TTY_CONFIG) |
+ (1L << OsConstants.CAP_WAKE_ALARM) |
+ (1L << OsConstants.CAP_BLOCK_SUSPEND);
/* Containers run without some capabilities, so drop any caps that are not available. */
StructCapUserHeader header = new StructCapUserHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
@@ -742,20 +741,6 @@
}
/**
- * Gets the bit array representation of the provided list of POSIX capabilities.
- */
- private static long posixCapabilitiesAsBits(int... capabilities) {
- long result = 0;
- for (int capability : capabilities) {
- if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
- throw new IllegalArgumentException(String.valueOf(capability));
- }
- result |= (1L << capability);
- }
- return result;
- }
-
- /**
* This is the entry point for a Zygote process. It creates the Zygote server, loads resources,
* and handles other tasks related to preparing the process for forking into applications.
*
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 4828393..4708be8 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -63,6 +63,9 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -114,6 +117,7 @@
import com.android.window.flags.Flags;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/** @hide */
@@ -1140,7 +1144,8 @@
mDrawLegacyNavigationBarBackground =
((requestedVisibleTypes | mLastForceConsumingTypes)
& WindowInsets.Type.navigationBars()) != 0
- && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
+ && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+ && navBarSize > 0;
if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
mDrawLegacyNavigationBarBackgroundHandled =
mWindow.onDrawLegacyNavigationBarBackgroundChanged(
@@ -1348,8 +1353,15 @@
mCrossWindowBlurEnabled = enabled;
updateBackgroundBlurRadius();
};
+ // The executor to receive callback {@link mCrossWindowBlurEnabledListener}. It
+ // should be the executor for this {@link DecorView}'s ui thread (not necessarily
+ // the main thread).
+ final Executor executor = Looper.myLooper() == Looper.getMainLooper()
+ ? getContext().getMainExecutor()
+ : new HandlerExecutor(new Handler(Looper.myLooper()));
getContext().getSystemService(WindowManager.class)
- .addCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);
+ .addCrossWindowBlurEnabledListener(
+ executor, mCrossWindowBlurEnabledListener);
getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
} else {
updateBackgroundBlurRadius();
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 2ff8c8c..e440dc9 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -34,7 +34,6 @@
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.INTERNED_DATA;
import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_MESSAGE;
@@ -72,7 +71,6 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -99,13 +97,11 @@
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;
+ @Deprecated
@Nullable
private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
@NonNull
@@ -151,13 +147,29 @@
cacheUpdater, groups);
}
+ @Deprecated
@VisibleForTesting
public PerfettoProtoLogImpl(
@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);
+ }
+
+ @VisibleForTesting
+ public PerfettoProtoLogImpl(
+ @Nullable String viewerConfigFilePath,
+ @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+ @NonNull Runnable cacheUpdater,
+ @NonNull IProtoLogGroup[] groups,
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @NonNull ProtoLogConfigurationService configurationService) {
+ this(viewerConfigFilePath, null, viewerConfigReader, cacheUpdater,
+ groups, dataSourceBuilder, configurationService);
}
private PerfettoProtoLogImpl(
@@ -166,11 +178,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 +218,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");
@@ -431,6 +461,7 @@
Log.d(LOG_TAG, "Finished onTracingFlush");
}
+ @Deprecated
private void dumpViewerConfig() {
if (mViewerConfigInputStreamProvider == null) {
// No viewer config available
@@ -439,103 +470,29 @@
Log.d(LOG_TAG, "Dumping viewer config to trace");
- mDataSource.trace(ctx -> {
- try {
- ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
-
- final ProtoOutputStream os = ctx.newTracePacket();
-
- os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
-
- final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- if (pis.getFieldNumber() == (int) MESSAGES) {
- writeViewerConfigMessage(pis, os);
- }
-
- if (pis.getFieldNumber() == (int) GROUPS) {
- writeViewerConfigGroup(pis, os);
- }
- }
-
- os.end(outProtologViewerConfigToken);
- } catch (IOException e) {
- Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
- }
- });
+ Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider);
Log.d(LOG_TAG, "Dumped viewer config to trace");
}
- private static void writeViewerConfigGroup(
- ProtoInputStream pis, ProtoOutputStream os) throws IOException {
- final long inGroupToken = pis.start(GROUPS);
- final long outGroupToken = os.start(GROUPS);
-
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) ID:
- int id = pis.readInt(ID);
- os.write(ID, id);
- break;
- case (int) NAME:
- String name = pis.readString(NAME);
- os.write(NAME, name);
- break;
- case (int) TAG:
- String tag = pis.readString(TAG);
- os.write(TAG, tag);
- break;
- default:
- throw new RuntimeException(
- "Unexpected field id " + pis.getFieldNumber());
- }
- }
-
- pis.end(inGroupToken);
- os.end(outGroupToken);
- }
-
- private static void writeViewerConfigMessage(
- ProtoInputStream pis, ProtoOutputStream os) throws IOException {
- final long inMessageToken = pis.start(MESSAGES);
- final long outMessagesToken = os.start(MESSAGES);
-
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) MessageData.MESSAGE_ID:
- os.write(MessageData.MESSAGE_ID,
- pis.readLong(MessageData.MESSAGE_ID));
- break;
- case (int) MESSAGE:
- os.write(MESSAGE, pis.readString(MESSAGE));
- break;
- case (int) LEVEL:
- os.write(LEVEL, pis.readInt(LEVEL));
- break;
- case (int) GROUP_ID:
- os.write(GROUP_ID, pis.readInt(GROUP_ID));
- break;
- case (int) LOCATION:
- os.write(LOCATION, pis.readString(LOCATION));
- break;
- default:
- throw new RuntimeException(
- "Unexpected field id " + pis.getFieldNumber());
- }
- }
-
- pis.end(inMessageToken);
- os.end(outMessagesToken);
- }
-
private void logToLogcat(String tag, LogLevel level, Message message,
@Nullable Object[] args) {
String messageString;
if (mViewerConfigReader == null) {
messageString = message.getMessage();
+
+ if (messageString == null) {
+ Log.e(LOG_TAG, "Failed to decode message for logcat. "
+ + "Message not available without ViewerConfig to decode the hash.");
+ }
} else {
messageString = message.getMessage(mViewerConfigReader);
+
+ if (messageString == null) {
+ Log.e(LOG_TAG, "Failed to decode message for logcat. "
+ + "Message hash either not available in viewerConfig file or "
+ + "not loaded into memory from file before decoding.");
+ }
}
if (messageString == null) {
@@ -733,12 +690,16 @@
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);
os.write(MessageData.MESSAGE_ID, messageHash);
os.write(MESSAGE, message);
- os.write(LEVEL, level.ordinal());
+ os.write(LEVEL, level.id);
os.write(GROUP_ID, logGroup.getId());
os.end(messageConfigToken);
@@ -930,17 +891,19 @@
}
private static class Message {
+ @Nullable
private final Long mMessageHash;
- private final Integer mMessageMask;
+ private final int mMessageMask;
+ @Nullable
private final String mMessageString;
- private Message(Long messageHash, int messageMask) {
+ private Message(long messageHash, int messageMask) {
this.mMessageHash = messageHash;
this.mMessageMask = messageMask;
this.mMessageString = null;
}
- private Message(String messageString) {
+ private Message(@NonNull String messageString) {
this.mMessageHash = null;
final List<Integer> argTypes = LogDataType.parseFormatString(messageString);
this.mMessageMask = LogDataType.logDataTypesToBitMask(argTypes);
@@ -951,16 +914,22 @@
return mMessageMask;
}
+ @Nullable
private String getMessage() {
return mMessageString;
}
+ @Nullable
private String getMessage(@NonNull ProtoLogViewerConfigReader viewerConfigReader) {
if (mMessageString != null) {
return mMessageString;
}
- return viewerConfigReader.getViewerString(mMessageHash);
+ if (mMessageHash != null) {
+ return viewerConfigReader.getViewerString(mMessageHash);
+ }
+
+ throw new RuntimeException("Both mMessageString and mMessageHash should never be null");
}
}
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
index 1765738..7031d69 100644
--- a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
+++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
@@ -23,10 +23,9 @@
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
-import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_VIEWER_CONFIG;
-import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,7 +34,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.os.SystemClock;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;
@@ -74,11 +72,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.
@@ -115,11 +109,29 @@
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);
+ }
+
+ @VisibleForTesting
+ public 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.
@@ -210,8 +222,7 @@
* want to write to the trace buffer.
* @throws FileNotFoundException if the viewerConfigFilePath is invalid.
*/
- void trace(@NonNull ProtoLogDataSource dataSource, @NonNull String viewerConfigFilePath)
- throws FileNotFoundException;
+ void trace(@NonNull ProtoLogDataSource dataSource, @NonNull String viewerConfigFilePath);
}
@Override
@@ -351,11 +362,7 @@
private void onTracingInstanceFlush() {
for (String fileName : mConfigFileCounts.keySet()) {
- try {
- mViewerConfigFileTracer.trace(mDataSource, fileName);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
+ mViewerConfigFileTracer.trace(mDataSource, fileName);
}
}
@@ -364,26 +371,13 @@
}
private static void dumpTransitionTraceConfig(@NonNull ProtoLogDataSource dataSource,
- @NonNull String viewerConfigFilePath) throws FileNotFoundException {
- final var pis = new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
-
- dataSource.trace(ctx -> {
+ @NonNull String viewerConfigFilePath) {
+ Utils.dumpViewerConfig(dataSource, () -> {
try {
- final ProtoOutputStream os = ctx.newTracePacket();
-
- os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
-
- final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) MESSAGES -> writeViewerConfigMessage(pis, os);
- case (int) GROUPS -> writeViewerConfigGroup(pis, os);
- }
- }
-
- os.end(outProtologViewerConfigToken);
- } catch (IOException e) {
- Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
+ return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(
+ "Failed to load viewer config file " + viewerConfigFilePath, e);
}
});
}
@@ -396,11 +390,7 @@
mConfigFileCounts.put(configFile, newCount);
boolean lastProcessWithViewerConfig = newCount == 0;
if (lastProcessWithViewerConfig) {
- try {
- mViewerConfigFileTracer.trace(mDataSource, configFile);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
+ mViewerConfigFileTracer.trace(mDataSource, configFile);
}
}
}
@@ -446,6 +436,7 @@
case (int) MESSAGE -> os.write(MESSAGE, pis.readString(MESSAGE));
case (int) LEVEL -> os.write(LEVEL, pis.readInt(LEVEL));
case (int) GROUP_ID -> os.write(GROUP_ID, pis.readInt(GROUP_ID));
+ case (int) LOCATION -> os.write(LOCATION, pis.readString(LOCATION));
default ->
throw new RuntimeException(
"Unexpected field id " + pis.getFieldNumber());
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
index ef6bece..0afb135 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;
@@ -37,6 +38,7 @@
import android.util.proto.ProtoInputStream;
import android.util.proto.WireTypeMismatchException;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.LogLevel;
import java.io.IOException;
@@ -48,21 +50,37 @@
public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance,
ProtoLogDataSource.TlsState,
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) {
- super("android.protolog");
+ public ProtoLogDataSource(
+ @NonNull Instance.TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull Instance.TracingInstanceStopCallback onStop) {
+ this(onStart, onFlush, onStop, DATASOURCE_NAME);
+ }
+
+ @VisibleForTesting
+ public ProtoLogDataSource(
+ @NonNull Instance.TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull Instance.TracingInstanceStopCallback onStop,
+ @NonNull String dataSourceName) {
+ super(dataSourceName);
this.mOnStart = onStart;
this.mOnFlush = onFlush;
this.mOnStop = onStop;
}
@Override
- public Instance createInstance(ProtoInputStream configStream, int instanceIndex) {
+ @NonNull
+ public Instance createInstance(@NonNull ProtoInputStream configStream, int instanceIndex) {
ProtoLogConfig config = null;
try {
@@ -92,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
@@ -103,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;
}
@@ -261,26 +283,44 @@
public static class Instance extends DataSourceInstance {
public interface TracingInstanceStartCallback {
- void run(int instanceIdx, ProtoLogConfig config);
+ /**
+ * Execute the tracing instance's onStart callback.
+ * @param instanceIdx The index of the tracing instance we are executing the callback
+ * for.
+ * @param config The protolog configuration for the tracing instance we are executing
+ * the callback for.
+ */
+ void run(int instanceIdx, @NonNull ProtoLogConfig config);
}
public interface TracingInstanceStopCallback {
- void run(int instanceIdx, ProtoLogConfig config);
+ /**
+ * Execute the tracing instance's onStop callback.
+ * @param instanceIdx The index of the tracing instance we are executing the callback
+ * for.
+ * @param config The protolog configuration for the tracing instance we are executing
+ * the callback for.
+ */
+ 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/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index da6d8cf..7bdcf2d 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -23,6 +23,7 @@
import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.VIEWER_CONFIG_PATH;
import android.annotation.Nullable;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.IProtoLog;
@@ -37,6 +38,8 @@
* A service for the ProtoLog logging system.
*/
public class ProtoLogImpl {
+ private static final String LOG_TAG = "ProtoLogImpl";
+
private static IProtoLog sServiceInstance = null;
@ProtoLogToolInjected(VIEWER_CONFIG_PATH)
@@ -97,6 +100,9 @@
*/
public static synchronized IProtoLog getSingleInstance() {
if (sServiceInstance == null) {
+ Log.i(LOG_TAG, "Setting up " + ProtoLogImpl.class.getSimpleName() + " with "
+ + "viewerConfigPath = " + sViewerConfigPath);
+
final var groups = sLogGroups.values().toArray(new IProtoLogGroup[0]);
if (android.tracing.Flags.perfettoProtologTracing()) {
@@ -105,6 +111,9 @@
// TODO(b/353530422): Remove - temporary fix to unblock b/352290057
// In some tests the viewer config file might not exist in which we don't
// want to provide config path to the user
+ Log.w(LOG_TAG, "Failed to find viewerConfigFile when setting up "
+ + ProtoLogImpl.class.getSimpleName() + ". "
+ + "Setting up without a viewer config instead...");
sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater, groups);
} else {
sServiceInstance =
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 38ca0d8..0a80e00 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -3,7 +3,6 @@
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
-
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
@@ -11,7 +10,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.util.Log;
import android.util.LongSparseArray;
import android.util.proto.ProtoInputStream;
@@ -26,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(
@@ -38,18 +38,26 @@
* Returns message format string for its hash or null if unavailable
* or the viewer config is not loaded into memory.
*/
+ @Nullable
public synchronized String getViewerString(long messageHash) {
return mLogMessageMap.get(messageHash);
}
- public synchronized void loadViewerConfig(String[] groups) {
+ /**
+ * Load the viewer configs for the target groups into memory.
+ * Only viewer configs loaded into memory can be required. So this must be called for all groups
+ * we want to query before we query their viewer config.
+ *
+ * @param groups Groups to load the viewer configs from file into memory.
+ */
+ 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;
@@ -70,14 +78,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;
@@ -91,8 +99,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();
@@ -141,7 +151,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/java/com/android/internal/protolog/Utils.java b/core/java/com/android/internal/protolog/Utils.java
new file mode 100644
index 0000000..1e6ba30
--- /dev/null
+++ b/core/java/com/android/internal/protolog/Utils.java
@@ -0,0 +1,138 @@
+/*
+ * 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 static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.TAG;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_VIEWER_CONFIG;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
+
+import android.annotation.NonNull;
+import android.internal.perfetto.protos.Protolog;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+
+public class Utils {
+ private static final String LOG_TAG = "ProtoLogUtils";
+
+ /**
+ * Dump the viewer config provided by the input stream to the target datasource.
+ * @param dataSource The datasource to dump the ProtoLog viewer config to.
+ * @param viewerConfigInputStreamProvider The InputStream that provided the proto viewer config.
+ */
+ public static void dumpViewerConfig(@NonNull ProtoLogDataSource dataSource,
+ @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) {
+ dataSource.trace(ctx -> {
+ try {
+ ProtoInputStream pis = viewerConfigInputStreamProvider.getInputStream();
+
+ final ProtoOutputStream os = ctx.newTracePacket();
+
+ os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
+
+ final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) MESSAGES) {
+ writeViewerConfigMessage(pis, os);
+ }
+
+ if (pis.getFieldNumber() == (int) GROUPS) {
+ writeViewerConfigGroup(pis, os);
+ }
+ }
+
+ os.end(outProtologViewerConfigToken);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump to datasource", e);
+ }
+ });
+ }
+
+ private static void writeViewerConfigGroup(
+ @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
+ final long inGroupToken = pis.start(GROUPS);
+ final long outGroupToken = os.start(GROUPS);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) ID:
+ int id = pis.readInt(ID);
+ os.write(ID, id);
+ break;
+ case (int) NAME:
+ String name = pis.readString(NAME);
+ os.write(NAME, name);
+ break;
+ case (int) TAG:
+ String tag = pis.readString(TAG);
+ os.write(TAG, tag);
+ break;
+ default:
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inGroupToken);
+ os.end(outGroupToken);
+ }
+
+ private static void writeViewerConfigMessage(
+ ProtoInputStream pis, ProtoOutputStream os) throws IOException {
+ final long inMessageToken = pis.start(MESSAGES);
+ final long outMessagesToken = os.start(MESSAGES);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID:
+ os.write(Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID,
+ pis.readLong(Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID));
+ break;
+ case (int) MESSAGE:
+ os.write(MESSAGE, pis.readString(MESSAGE));
+ break;
+ case (int) LEVEL:
+ os.write(LEVEL, pis.readInt(LEVEL));
+ break;
+ case (int) GROUP_ID:
+ os.write(GROUP_ID, pis.readInt(GROUP_ID));
+ break;
+ case (int) LOCATION:
+ os.write(LOCATION, pis.readString(LOCATION));
+ break;
+ default:
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inMessageToken);
+ os.end(outMessagesToken);
+ }
+
+}
diff --git a/core/java/com/android/internal/protolog/common/LogLevel.java b/core/java/com/android/internal/protolog/common/LogLevel.java
index 16c34e1..b5541ae 100644
--- a/core/java/com/android/internal/protolog/common/LogLevel.java
+++ b/core/java/com/android/internal/protolog/common/LogLevel.java
@@ -17,10 +17,18 @@
package com.android.internal.protolog.common;
public enum LogLevel {
- DEBUG("d"), VERBOSE("v"), INFO("i"), WARN("w"), ERROR("e"), WTF("wtf");
+ DEBUG("d", 1),
+ VERBOSE("v", 2),
+ INFO("i", 3),
+ WARN("w", 4),
+ ERROR("e", 5),
+ WTF("wtf", 6);
public final String shortCode;
- LogLevel(String shortCode) {
+ public final int id;
+
+ LogLevel(String shortCode, int id) {
this.shortCode = shortCode;
+ this.id = id;
}
}
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index ee3a3c2..30b160a 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -16,15 +16,15 @@
package com.android.internal.ravenwood;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.ravenwood.annotation.RavenwoodReplace;
/**
* Class to interact with the Ravenwood environment.
*/
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.RavenwoodEnvironment_host")
+@RavenwoodRedirectionClass("RavenwoodEnvironment_host")
public final class RavenwoodEnvironment {
public static final String TAG = "RavenwoodEnvironment";
@@ -40,7 +40,7 @@
ensureRavenwoodInitialized();
}
- private static RuntimeException notSupportedOnDevice() {
+ public static RuntimeException notSupportedOnDevice() {
return new UnsupportedOperationException("This method can only be used on Ravenwood");
}
@@ -56,12 +56,10 @@
*
* No-op if called on the device side.
*/
- @RavenwoodReplace
+ @RavenwoodRedirect
public static void ensureRavenwoodInitialized() {
}
- private static native void ensureRavenwoodInitialized$ravenwood();
-
/**
* USE IT SPARINGLY! Returns true if it's running on Ravenwood, hostside test environment.
*
@@ -87,13 +85,11 @@
* Get the object back from the address obtained from
* {@link dalvik.system.VMRuntime#addressOf(Object)}.
*/
- @RavenwoodReplace
+ @RavenwoodRedirect
public <T> T fromAddress(long address) {
throw notSupportedOnDevice();
}
- private native <T> T fromAddress$ravenwood(long address);
-
/**
* See {@link Workaround}. It's only usable on Ravenwood.
*/
@@ -109,13 +105,11 @@
/**
* @return the "ravenwood-runtime" directory.
*/
- @RavenwoodReplace
+ @RavenwoodRedirect
public String getRavenwoodRuntimePath() {
throw notSupportedOnDevice();
}
- private native String getRavenwoodRuntimePath$ravenwood();
-
/**
* A set of APIs used to work around missing features on Ravenwood. Ideally, this class should
* be empty, and all its APIs should be able to be implemented properly.
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java
index f8a1436..1938cdb 100644
--- a/core/java/com/android/internal/statusbar/StatusBarIcon.java
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java
@@ -51,6 +51,19 @@
ResourceIcon
}
+ public enum Shape {
+ /**
+ * Icon view should use WRAP_CONTENT -- so that the horizontal space occupied depends on the
+ * icon's shape (skinny/fat icons take less/more). Most icons will want to use this option
+ * for a nicer-looking overall spacing in the status bar, as long as the icon is "known"
+ * (i.e. not coming from a 3P package).
+ */
+ WRAP_CONTENT,
+
+ /** Icon should always be displayed in a space as wide as the status bar is tall. */
+ FIXED_SPACE,
+ }
+
public UserHandle user;
public String pkg;
public Icon icon;
@@ -59,6 +72,7 @@
public int number;
public CharSequence contentDescription;
public Type type;
+ public Shape shape;
/**
* Optional {@link Drawable} corresponding to {@link #icon}. This field is not parcelable, so
@@ -68,7 +82,7 @@
@Nullable public Drawable preloadedIcon;
public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
- CharSequence contentDescription, Type type) {
+ CharSequence contentDescription, Type type, Shape shape) {
if (icon.getType() == Icon.TYPE_RESOURCE
&& TextUtils.isEmpty(icon.getResPackage())) {
// This is an odd situation where someone's managed to hand us an icon without a
@@ -83,6 +97,13 @@
this.number = number;
this.contentDescription = contentDescription;
this.type = type;
+ this.shape = shape;
+ }
+
+ public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
+ CharSequence contentDescription, Type type) {
+ this(user, resPackage, icon, iconLevel, number, contentDescription, type,
+ Shape.WRAP_CONTENT);
}
public StatusBarIcon(String iconPackage, UserHandle user,
@@ -107,7 +128,7 @@
@Override
public StatusBarIcon clone() {
StatusBarIcon that = new StatusBarIcon(this.user, this.pkg, this.icon,
- this.iconLevel, this.number, this.contentDescription, this.type);
+ this.iconLevel, this.number, this.contentDescription, this.type, this.shape);
that.visible = this.visible;
that.preloadedIcon = this.preloadedIcon;
return that;
@@ -129,6 +150,7 @@
this.number = in.readInt();
this.contentDescription = in.readCharSequence();
this.type = Type.valueOf(in.readString());
+ this.shape = Shape.valueOf(in.readString());
}
public void writeToParcel(Parcel out, int flags) {
@@ -140,6 +162,7 @@
out.writeInt(this.number);
out.writeCharSequence(this.contentDescription);
out.writeString(this.type.name());
+ out.writeString(this.shape.name());
}
public int describeContents() {
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b51678e..efbf887 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -76,10 +76,10 @@
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in ImeTracker.Token statsToken, int flags, int lastClickToolType,
- in @nullable ResultReceiver resultReceiver, int reason);
+ in @nullable ResultReceiver resultReceiver, int reason, boolean async);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in ImeTracker.Token statsToken, int flags,
- in @nullable ResultReceiver resultReceiver, int reason);
+ in @nullable ResultReceiver resultReceiver, int reason, boolean async);
/**
* A test API for CTS to request hiding the current soft input window, with the request origin
@@ -120,7 +120,8 @@
in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, int userId,
- in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+ in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 11c220b..0ec55f9 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -120,6 +120,7 @@
private static final String TAG = "LockPatternView";
private OnPatternListener mOnPatternListener;
+ private ExternalHapticsPlayer mExternalHapticsPlayer;
@UnsupportedAppUsage
private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9);
@@ -317,6 +318,13 @@
void onPatternDetected(List<Cell> pattern);
}
+ /** An external haptics player for pattern updates. */
+ public interface ExternalHapticsPlayer{
+
+ /** Perform haptic feedback when a cell is added to the pattern. */
+ void performCellAddedFeedback();
+ }
+
public LockPatternView(Context context) {
this(context, null);
}
@@ -461,6 +469,15 @@
}
/**
+ * Set the external haptics player for feedback on pattern detection.
+ * @param player The external player.
+ */
+ @UnsupportedAppUsage
+ public void setExternalHapticsPlayer(ExternalHapticsPlayer player) {
+ mExternalHapticsPlayer = player;
+ }
+
+ /**
* Set the pattern explicitely (rather than waiting for the user to input
* a pattern).
* @param displayMode How to display the pattern.
@@ -847,6 +864,16 @@
return null;
}
+ @Override
+ public boolean performHapticFeedback(int feedbackConstant, int flags) {
+ if (mExternalHapticsPlayer != null) {
+ mExternalHapticsPlayer.performCellAddedFeedback();
+ return true;
+ } else {
+ return super.performHapticFeedback(feedbackConstant, flags);
+ }
+ }
+
private void addCellToPattern(Cell newCell) {
mPatternDrawLookup[newCell.getRow()][newCell.getColumn()] = true;
mPattern.add(newCell);
diff --git a/core/java/com/android/internal/widget/ViewGroupFader.java b/core/java/com/android/internal/widget/ViewGroupFader.java
index b54023a..21206c2 100644
--- a/core/java/com/android/internal/widget/ViewGroupFader.java
+++ b/core/java/com/android/internal/widget/ViewGroupFader.java
@@ -16,12 +16,14 @@
package com.android.internal.widget;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.animation.BaseInterpolator;
import android.view.animation.PathInterpolator;
+import android.widget.flags.Flags;
/**
* This class is ported from
@@ -36,7 +38,7 @@
* height of the child. When not in the top or bottom regions, children have their default alpha and
* scale.
*/
-class ViewGroupFader {
+public class ViewGroupFader {
private static final float SCALE_LOWER_BOUND = 0.7f;
private float mScaleLowerBound = SCALE_LOWER_BOUND;
@@ -68,7 +70,7 @@
private BaseInterpolator mBottomInterpolator = new PathInterpolator(0.3f, 0f, 0.7f, 1f);
/** Callback which is called when attempting to fade a view. */
- interface AnimationCallback {
+ public interface AnimationCallback {
boolean shouldFadeFromTop(View view);
boolean shouldFadeFromBottom(View view);
@@ -82,7 +84,7 @@
* of the current position.
*/
// TODO(b/182846214): Clean up the interface design to avoid exposing too much details to users.
- interface ChildViewBoundsProvider {
+ public interface ChildViewBoundsProvider {
/**
* Provide the bounds of the child view.
*
@@ -168,7 +170,7 @@
}
}
- ViewGroupFader(
+ public ViewGroupFader(
ViewGroup parent,
AnimationCallback callback,
ChildViewBoundsProvider childViewBoundsProvider) {
@@ -212,7 +214,7 @@
this.mContainerBoundsProvider = boundsProvider;
}
- void updateFade() {
+ public void updateFade() {
mContainerBoundsProvider.provideBounds(mParent, mContainerBounds);
mTopBoundPixels = mContainerBounds.height() * mChainedBoundsTop;
mBottomBoundPixels = mContainerBounds.height() * mChainedBoundsBottom;
@@ -221,13 +223,20 @@
}
/** For each list element, calculate and adjust the scale and alpha based on its position */
- private void updateListElementFades(ViewGroup parent, boolean shouldFade) {
+ public void updateListElementFades(ViewGroup parent, boolean shouldFade) {
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);
if (child.getVisibility() != View.VISIBLE) {
continue;
}
+ if (Flags.enableFadingViewGroup() && Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.config_enableViewGroupScalingFading)) {
+ if (child instanceof ViewGroup) {
+ updateListElementFades((ViewGroup) child, true);
+ }
+ }
+
if (shouldFade) {
fadeElement(parent, child);
}
@@ -312,4 +321,4 @@
private static float lerp(float min, float max, float fraction) {
return min + (max - min) * fraction;
}
-}
+}
\ No newline at end of file
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2abdd57..90cb10a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -108,6 +108,7 @@
"libtracing_perfetto",
"libharfbuzz_ng",
"liblog",
+ "libmediautils",
"libminikin",
"libz",
"server_configurable_flags",
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 7410468..3370f38 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -70,11 +70,14 @@
// Open flags.
// Must be kept in sync with the constants defined in SQLiteDatabase.java.
enum {
+ // LINT.IfChange
OPEN_READWRITE = 0x00000000,
OPEN_READONLY = 0x00000001,
OPEN_READ_MASK = 0x00000001,
NO_LOCALIZED_COLLATORS = 0x00000010,
+ NO_DOUBLE_QUOTED_STRS = 0x00000020,
CREATE_IF_NECESSARY = 0x10000000,
+ // LINT.ThenChange(/core/java/android/database/sqlite/SQLiteDatabase.java)
};
sqlite3* const db;
@@ -156,6 +159,18 @@
}
}
+ // Disallow double-quoted string literals if the proper flag is set.
+ if ((openFlags & SQLiteConnection::NO_DOUBLE_QUOTED_STRS) != 0) {
+ void *setting = 0;
+ int err = 0;
+ if ((err = sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, setting)) != SQLITE_OK) {
+ ALOGE("failed to configure SQLITE_DBCONFIG_DQS_DDL: %d", err);
+ }
+ if ((err = sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, setting)) != SQLITE_OK) {
+ ALOGE("failed to configure SQLITE_DBCONFIG_DQS_DML: %d", err);
+ }
+ }
+
// Check that the database is really read/write when that is what we asked for.
if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) {
throw_sqlite3_exception(env, db, "Could not open the database in read/write mode.");
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 638591f..9bccf5a 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
+#include <atomic>
#define LOG_TAG "AudioSystem-JNI"
#include <android/binder_ibinder_jni.h>
#include <android/binder_libbinder.h>
@@ -34,15 +35,16 @@
#include <media/AudioContainers.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
+#include <mediautils/jthread.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/jni_macros.h>
#include <system/audio.h>
#include <system/audio_policy.h>
+#include <sys/system_properties.h>
#include <utils/Log.h>
-#include <thread>
#include <optional>
#include <sstream>
#include <memory>
@@ -57,6 +59,7 @@
#include "android_media_AudioMixerAttributes.h"
#include "android_media_AudioProfile.h"
#include "android_media_MicrophoneInfo.h"
+#include "android_media_JNIUtils.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
@@ -3375,42 +3378,48 @@
class JavaSystemPropertyListener {
public:
JavaSystemPropertyListener(JNIEnv* env, jobject javaCallback, std::string sysPropName) :
- mCallback(env->NewGlobalRef(javaCallback)),
- mCachedProperty(android::base::CachedProperty{std::move(sysPropName)}) {
- mListenerThread = std::thread([this]() mutable {
- JNIEnv* threadEnv = GetOrAttachJNIEnvironment(gVm);
- while (!mCleanupSignal.load()) {
- using namespace std::chrono_literals;
- // 1s timeout so this thread can read the cleanup signal to (slowly) be able to
- // be destroyed.
- std::string newVal = mCachedProperty.WaitForChange(1000ms) ?: "";
- if (newVal != "" && mLastVal != newVal) {
- threadEnv->CallVoidMethod(mCallback, gRunnableClassInfo.run);
- mLastVal = std::move(newVal);
+ mCallback {javaCallback, env},
+ mSysPropName(sysPropName),
+ mCachedProperty(android::base::CachedProperty{std::move(sysPropName)}),
+ mListenerThread([this](mediautils::stop_token stok) mutable {
+ while (!stok.stop_requested()) {
+ using namespace std::chrono_literals;
+ // 1s timeout so this thread can eventually respond to the stop token
+ std::string newVal = mCachedProperty.WaitForChange(1000ms) ?: "";
+ updateValue(newVal);
}
- }
- });
- }
+ }) {}
- ~JavaSystemPropertyListener() {
- mCleanupSignal.store(true);
- mListenerThread.join();
- JNIEnv* env = GetOrAttachJNIEnvironment(gVm);
- env->DeleteGlobalRef(mCallback);
+ void triggerUpdateIfChanged() {
+ // We must check the property without using the cached property due to thread safety issues
+ std::string newVal = base::GetProperty(mSysPropName, "");
+ updateValue(newVal);
}
private:
- jobject mCallback;
+ void updateValue(std::string newVal) {
+ if (newVal == "") return;
+ std::lock_guard l{mLock};
+ if (mLastVal == newVal) return;
+ const auto threadEnv = GetOrAttachJNIEnvironment(gVm);
+ threadEnv->CallVoidMethod(mCallback.get(), gRunnableClassInfo.run);
+ mLastVal = std::move(newVal);
+ }
+
+ // Should outlive thread object
+ const GlobalRef mCallback;
+ const std::string mSysPropName;
android::base::CachedProperty mCachedProperty;
- std::thread mListenerThread;
- std::atomic<bool> mCleanupSignal{false};
std::string mLastVal = "";
+ std::mutex mLock;
+ const mediautils::jthread mListenerThread;
};
+// A logical set keyed by address
std::vector<std::unique_ptr<JavaSystemPropertyListener>> gSystemPropertyListeners;
std::mutex gSysPropLock{};
-static void android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env, jobject thiz,
+static jlong android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env, jobject thiz,
jstring sysProp,
jobject javaCallback) {
ScopedUtfChars sysPropChars{env, sysProp};
@@ -3418,6 +3427,19 @@
std::string{sysPropChars.c_str()});
std::unique_lock _l{gSysPropLock};
gSystemPropertyListeners.push_back(std::move(listener));
+ return reinterpret_cast<jlong>(gSystemPropertyListeners.back().get());
+}
+
+static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env, jobject thiz,
+ jlong nativeHandle) {
+ std::unique_lock _l{gSysPropLock};
+ const auto iter = std::find_if(gSystemPropertyListeners.begin(), gSystemPropertyListeners.end(),
+ [nativeHandle](const auto& x) { return reinterpret_cast<jlong>(x.get()) == nativeHandle; });
+ if (iter != gSystemPropertyListeners.end()) {
+ (*iter)->triggerUpdateIfChanged();
+ } else {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid handle");
+ }
}
@@ -3595,8 +3617,11 @@
MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
- "(Ljava/lang/String;Ljava/lang/Runnable;)V",
+ "(Ljava/lang/String;Ljava/lang/Runnable;)J",
android_media_AudioSystem_listenForSystemPropertyChange),
+ MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate",
+ "(J)V",
+ android_media_AudioSystem_triggerSystemPropertyUpdate),
};
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 584ebaa..dec724b 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -90,7 +90,7 @@
env->CallVoidMethod(parcelObj, gParcelOffsets.recycle);
}
-static void android_os_Parcel_markSensitive(jlong nativePtr)
+static void android_os_Parcel_markSensitive(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel) {
@@ -116,30 +116,30 @@
}
}
-static jboolean android_os_Parcel_isForRpc(jlong nativePtr) {
+static jboolean android_os_Parcel_isForRpc(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return parcel ? parcel->isForRpc() : false;
}
-static jint android_os_Parcel_dataSize(jlong nativePtr)
+static jint android_os_Parcel_dataSize(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return parcel ? parcel->dataSize() : 0;
}
-static jint android_os_Parcel_dataAvail(jlong nativePtr)
+static jint android_os_Parcel_dataAvail(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return parcel ? parcel->dataAvail() : 0;
}
-static jint android_os_Parcel_dataPosition(jlong nativePtr)
+static jint android_os_Parcel_dataPosition(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return parcel ? parcel->dataPosition() : 0;
}
-static jint android_os_Parcel_dataCapacity(jlong nativePtr)
+static jint android_os_Parcel_dataCapacity(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return parcel ? parcel->dataCapacity() : 0;
@@ -156,7 +156,7 @@
}
}
-static void android_os_Parcel_setDataPosition(jlong nativePtr, jint pos)
+static void android_os_Parcel_setDataPosition(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr, jint pos)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -175,7 +175,8 @@
}
}
-static jboolean android_os_Parcel_pushAllowFds(jlong nativePtr, jboolean allowFds)
+static jboolean android_os_Parcel_pushAllowFds(CRITICAL_JNI_PARAMS_COMMA
+ jlong nativePtr, jboolean allowFds)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
jboolean ret = JNI_TRUE;
@@ -185,7 +186,8 @@
return ret;
}
-static void android_os_Parcel_restoreAllowFds(jlong nativePtr, jboolean lastValue)
+static void android_os_Parcel_restoreAllowFds(CRITICAL_JNI_PARAMS_COMMA
+ jlong nativePtr, jboolean lastValue)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -259,22 +261,22 @@
blob.release();
}
-static int android_os_Parcel_writeInt(jlong nativePtr, jint val) {
+static int android_os_Parcel_writeInt(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr, jint val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return (parcel != NULL) ? parcel->writeInt32(val) : OK;
}
-static int android_os_Parcel_writeLong(jlong nativePtr, jlong val) {
+static int android_os_Parcel_writeLong(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr, jlong val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return (parcel != NULL) ? parcel->writeInt64(val) : OK;
}
-static int android_os_Parcel_writeFloat(jlong nativePtr, jfloat val) {
+static int android_os_Parcel_writeFloat(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr, jfloat val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return (parcel != NULL) ? parcel->writeFloat(val) : OK;
}
-static int android_os_Parcel_writeDouble(jlong nativePtr, jdouble val) {
+static int android_os_Parcel_writeDouble(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr, jdouble val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return (parcel != NULL) ? parcel->writeDouble(val) : OK;
}
@@ -446,7 +448,7 @@
return ret;
}
-static jint android_os_Parcel_readInt(jlong nativePtr)
+static jint android_os_Parcel_readInt(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -455,7 +457,7 @@
return 0;
}
-static jlong android_os_Parcel_readLong(jlong nativePtr)
+static jlong android_os_Parcel_readLong(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -464,7 +466,7 @@
return 0;
}
-static jfloat android_os_Parcel_readFloat(jlong nativePtr)
+static jfloat android_os_Parcel_readFloat(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -473,7 +475,7 @@
return 0;
}
-static jdouble android_os_Parcel_readDouble(jlong nativePtr)
+static jdouble android_os_Parcel_readDouble(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -690,7 +692,7 @@
return JNI_FALSE;
}
-static jboolean android_os_Parcel_hasFileDescriptors(jlong nativePtr)
+static jboolean android_os_Parcel_hasFileDescriptors(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
jboolean ret = JNI_FALSE;
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
@@ -807,7 +809,7 @@
return Parcel::getGlobalAllocCount();
}
-static jlong android_os_Parcel_getOpenAshmemSize(jlong nativePtr)
+static jlong android_os_Parcel_getOpenAshmemSize(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -816,7 +818,7 @@
return 0;
}
-static jint android_os_Parcel_readCallingWorkSourceUid(jlong nativePtr)
+static jint android_os_Parcel_readCallingWorkSourceUid(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -825,7 +827,8 @@
return IPCThreadState::kUnsetWorkSource;
}
-static jboolean android_os_Parcel_replaceCallingWorkSourceUid(jlong nativePtr, jint uid)
+static jboolean android_os_Parcel_replaceCallingWorkSourceUid(CRITICAL_JNI_PARAMS_COMMA
+ jlong nativePtr, jint uid)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 46b4695..f2c70b5 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -56,11 +56,11 @@
//#undef ALOGV
//#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
-#define DEBUG_DEATH 0
-#if DEBUG_DEATH
-#define LOGDEATH ALOGD
+#define DEBUG_DEATH_FREEZE 0
+#if DEBUG_DEATH_FREEZE
+#define LOG_DEATH_FREEZE ALOGD
#else
-#define LOGDEATH ALOGV
+#define LOG_DEATH_FREEZE ALOGV
#endif
using namespace android;
@@ -116,6 +116,7 @@
jclass mClass;
jmethodID mGetInstance;
jmethodID mSendDeathNotice;
+ jmethodID mInvokeFrozenStateChangeCallback;
// Object state.
jfieldID mNativeData; // Field holds native pointer to BinderProxyNativeData.
@@ -547,23 +548,59 @@
// ----------------------------------------------------------------------------
-// Per-IBinder death recipient bookkeeping. This is how we reconcile local jobject
-// death recipient references passed in through JNI with the permanent corresponding
-// JavaDeathRecipient objects.
+// A JavaRecipient receives either death notifications or frozen state change
+// callbacks from natve code (IBinder) and dispatch the notifications to its
+// corresponding Java listener object.
+//
+// A RecipientList keeps tracks of all JavaRecipients for an IBinder. This way
+// we can find a JavaRecipient given a Java listener object.
+//
+// The implementation is shared between death recipients and frozen state change
+// callbacks via template. For death recipients the template is instantiated as
+// follows:
+//
+// IBinder::DeathRecipient
+// ^
+// |
+// (inherits)
+// |
+// JavaRecipient<IBinder::DeathRecipient> <----> RecipientList<IBinder::DeathRecipient>
+// ^
+// |
+// (inherits)
+// |
+// JavaDeathRecipient
+//
+//
+// The instantiation for frozen state change callbacks are:
+//
+// IBinder::FrozenStateChangeCallback
+// ^
+// |
+// (inherits)
+// |
+// JavaRecipient<IBinder::FrozenStateChangeCallback>
+// ^ ^
+// | |
+// (inherits) +--> RecipientList<IBinder::FrozenStateChangeCallback>
+// |
+// JavaFrozenStateChangeCallback
-class JavaDeathRecipient;
+template <typename T>
+class JavaRecipient;
-class DeathRecipientList : public RefBase {
- List< sp<JavaDeathRecipient> > mList;
+template <typename T>
+class RecipientList : public RefBase {
+ List<sp<JavaRecipient<T> > > mList;
Mutex mLock;
public:
- DeathRecipientList();
- ~DeathRecipientList();
+ RecipientList();
+ ~RecipientList();
- void add(const sp<JavaDeathRecipient>& recipient);
- void remove(const sp<JavaDeathRecipient>& recipient);
- sp<JavaDeathRecipient> find(jobject recipient);
+ void add(const sp<JavaRecipient<T> >& recipient);
+ void remove(const sp<JavaRecipient<T> >& recipient);
+ sp<JavaRecipient<T> > find(jobject recipient);
Mutex& lock(); // Use with care; specifically for mutual exclusion during binder death
};
@@ -584,11 +621,113 @@
#endif // __BIONIC__
#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
-class JavaDeathRecipient : public IBinder::DeathRecipient
-{
+template <typename T>
+constexpr const char* logPrefix();
+
+template <>
+constexpr const char* logPrefix<IBinder::DeathRecipient>() {
+ return "[DEATH]";
+}
+
+template <>
+constexpr const char* logPrefix<IBinder::FrozenStateChangeCallback>() {
+ return "[FREEZE]";
+}
+
+template <typename T>
+class JavaRecipient : public T {
public:
- JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
+ JavaRecipient(JNIEnv* env, jobject object, const sp<RecipientList<T> >& list,
+ bool useWeakReference)
: mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) {
+ if (useWeakReference) {
+ mObjectWeak = env->NewWeakGlobalRef(object);
+ } else {
+ mObject = env->NewGlobalRef(object);
+ }
+ // These objects manage their own lifetimes so are responsible for final bookkeeping.
+ // The list holds a strong reference to this object.
+ LOG_DEATH_FREEZE("%s Adding JavaRecipient %p to RecipientList %p", logPrefix<T>(), this,
+ list.get());
+ list->add(this);
+ }
+
+ void clearReference() {
+ sp<RecipientList<T> > list = mList.promote();
+ if (list != NULL) {
+ LOG_DEATH_FREEZE("%s Removing JavaRecipient %p from RecipientList %p", logPrefix<T>(),
+ this, list.get());
+ list->remove(this);
+ } else {
+ LOG_DEATH_FREEZE("%s clearReference() on JavaRecipient %p but RecipientList wp purged",
+ logPrefix<T>(), this);
+ }
+ }
+
+ bool matches(jobject obj) {
+ bool result;
+ JNIEnv* env = javavm_to_jnienv(mVM);
+
+ if (mObject != NULL) {
+ result = env->IsSameObject(obj, mObject);
+ } else {
+ ScopedLocalRef<jobject> me(env, env->NewLocalRef(mObjectWeak));
+ result = env->IsSameObject(obj, me.get());
+ }
+ return result;
+ }
+
+ void warnIfStillLive() {
+ if (mObject != NULL) {
+ // Okay, something is wrong -- we have a hard reference to a live death
+ // recipient on the VM side, but the list is being torn down.
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
+ ScopedLocalRef<jstring> nameRef(env,
+ (jstring)env->CallObjectMethod(objClassRef.get(),
+ gClassOffsets.mGetName));
+ ScopedUtfChars nameUtf(env, nameRef.get());
+ if (nameUtf.c_str() != NULL) {
+ ALOGW("BinderProxy is being destroyed but the application did not call "
+ "unlinkToDeath to unlink all of its death recipients beforehand. "
+ "Releasing leaked death recipient: %s",
+ nameUtf.c_str());
+ } else {
+ ALOGW("BinderProxy being destroyed; unable to get DR object name");
+ env->ExceptionClear();
+ }
+ }
+ }
+
+protected:
+ virtual ~JavaRecipient() {
+ // ALOGI("Removing death ref: recipient=%p\n", mObject);
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ if (mObject != NULL) {
+ env->DeleteGlobalRef(mObject);
+ } else {
+ env->DeleteWeakGlobalRef(mObjectWeak);
+ }
+ }
+
+ JavaVM* const mVM;
+
+ // If useWeakReference is false (e.g. JavaDeathRecipient when target sdk version < 35), the
+ // Java-side Recipient is strongly referenced from mObject initially, and may later be demoted
+ // to a weak reference (mObjectWeak), e.g. upon linkToDeath() and then after binderDied() is
+ // called.
+ // If useWeakReference is true, the strong reference is never made here (i.e. mObject == NULL
+ // always). Instead, the strong reference to the Java-side Recipient is made in
+ // BinderProxy.{mDeathRecipients,mFrozenStateChangeCallbacks}. In the native world, only the
+ // weak reference is kept.
+ jobject mObject;
+ jweak mObjectWeak;
+ wp<RecipientList<T> > mList;
+};
+
+class JavaDeathRecipient : public JavaRecipient<IBinder::DeathRecipient> {
+public:
+ static bool useWeakReference() {
// b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref
// to the death recipient objects. This is to prevent the memory leak which can happen when
// the death recipient object internally has a strong reference to the proxy object. Under
@@ -604,25 +743,26 @@
// reference to. If however you want to get binderDied() regardless of the proxy object's
// lifecycle, keep a strong reference to the death recipient object by yourself.
#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
- if (target_sdk_is_at_least_vic()) {
- mObjectWeak = env->NewWeakGlobalRef(object);
- } else
+ return target_sdk_is_at_least_vic();
+#else
+ return false;
#endif
- {
- mObject = env->NewGlobalRef(object);
- }
- // These objects manage their own lifetimes so are responsible for final bookkeeping.
- // The list holds a strong reference to this object.
- LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
- list->add(this);
+ }
+ JavaDeathRecipient(JNIEnv* env, jobject object,
+ const sp<RecipientList<IBinder::DeathRecipient> >& list)
+ : JavaRecipient(env, object, list, useWeakReference()) {
gNumDeathRefsCreated.fetch_add(1, std::memory_order_relaxed);
gcIfManyNewRefs(env);
}
+ ~JavaDeathRecipient() {
+ gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
+ }
+
void binderDied(const wp<IBinder>& who)
{
- LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
+ LOG_DEATH_FREEZE("Receiving binderDied() on JavaDeathRecipient %p\n", this);
if (mObject == NULL && mObjectWeak == NULL) {
return;
}
@@ -662,7 +802,7 @@
// with our containing DeathRecipientList so that we can't delete the global ref on mObject
// while the list is being iterated.
if (mObject != NULL) {
- sp<DeathRecipientList> list = mList.promote();
+ auto list = mList.promote();
if (list != NULL) {
AutoMutex _l(list->lock());
@@ -673,126 +813,96 @@
}
}
- void clearReference()
- {
- sp<DeathRecipientList> list = mList.promote();
- if (list != NULL) {
- LOGDEATH("Removing JDR %p from DRL %p", this, list.get());
- list->remove(this);
- } else {
- LOGDEATH("clearReference() on JDR %p but DRL wp purged", this);
- }
- }
-
- bool matches(jobject obj) {
- bool result;
- JNIEnv* env = javavm_to_jnienv(mVM);
-
- if (mObject != NULL) {
- result = env->IsSameObject(obj, mObject);
- } else {
- ScopedLocalRef<jobject> me(env, env->NewLocalRef(mObjectWeak));
- result = env->IsSameObject(obj, me.get());
- }
- return result;
- }
-
- void warnIfStillLive() {
- if (mObject != NULL) {
- // Okay, something is wrong -- we have a hard reference to a live death
- // recipient on the VM side, but the list is being torn down.
- JNIEnv* env = javavm_to_jnienv(mVM);
- ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
- ScopedLocalRef<jstring> nameRef(env,
- (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
- ScopedUtfChars nameUtf(env, nameRef.get());
- if (nameUtf.c_str() != NULL) {
- ALOGW("BinderProxy is being destroyed but the application did not call "
- "unlinkToDeath to unlink all of its death recipients beforehand. "
- "Releasing leaked death recipient: %s", nameUtf.c_str());
- } else {
- ALOGW("BinderProxy being destroyed; unable to get DR object name");
- env->ExceptionClear();
- }
- }
- }
-
-protected:
- virtual ~JavaDeathRecipient()
- {
- //ALOGI("Removing death ref: recipient=%p\n", mObject);
- gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
- JNIEnv* env = javavm_to_jnienv(mVM);
- if (mObject != NULL) {
- env->DeleteGlobalRef(mObject);
- } else {
- env->DeleteWeakGlobalRef(mObjectWeak);
- }
- }
-
private:
- JavaVM* const mVM;
-
- // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject
- // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to
- // a weak reference (mObjectWeak).
- // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL
- // always). Instead, the strong reference to the Java-side DeathRecipient is made in
- // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept.
- jobject mObject;
- jweak mObjectWeak;
- wp<DeathRecipientList> mList;
-
// Whether binderDied was called or not.
bool mFired = false;
};
+class JavaFrozenStateChangeCallback : public JavaRecipient<IBinder::FrozenStateChangeCallback> {
+public:
+ JavaFrozenStateChangeCallback(
+ JNIEnv* env, jobject object,
+ const sp<RecipientList<IBinder::FrozenStateChangeCallback> >& list)
+ : JavaRecipient(env, object, list, /*useWeakReference=*/true) {}
+
+ void onStateChanged(const wp<IBinder>& who, State state) {
+ LOG_DEATH_FREEZE("Receiving onStateChanged() on JavaFrozenStateChangeCallback %p. state: "
+ "%s\n",
+ this, state == State::FROZEN ? "FROZEN" : "UNFROZEN");
+ if (mObjectWeak == NULL) {
+ return;
+ }
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
+
+ // Hold a local reference to the recipient. This may fail if the recipient is weakly
+ // referenced, in which case we can't deliver the notification.
+ ScopedLocalRef<jobject> jCallback(env, env->NewLocalRef(mObjectWeak));
+ if (jCallback.get() == NULL) {
+ return;
+ }
+ env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
+ gBinderProxyOffsets.mInvokeFrozenStateChangeCallback,
+ jCallback.get(), jBinderProxy.get(), state);
+ if (env->ExceptionCheck()) {
+ jthrowable excep = env->ExceptionOccurred();
+ binder_report_exception(env, excep,
+ "*** Uncaught exception returned from frozen state change "
+ "notification!");
+ }
+ }
+};
+
// ----------------------------------------------------------------------------
-DeathRecipientList::DeathRecipientList() {
- LOGDEATH("New DRL @ %p", this);
+template <typename T>
+RecipientList<T>::RecipientList() {
+ LOG_DEATH_FREEZE("%s New RecipientList @ %p", logPrefix<T>(), this);
}
-DeathRecipientList::~DeathRecipientList() {
- LOGDEATH("Destroy DRL @ %p", this);
+template <typename T>
+RecipientList<T>::~RecipientList() {
+ LOG_DEATH_FREEZE("%s Destroy RecipientList @ %p", logPrefix<T>(), this);
AutoMutex _l(mLock);
- // Should never happen -- the JavaDeathRecipient objects that have added themselves
+ // Should never happen -- the JavaRecipientList objects that have added themselves
// to the list are holding references on the list object. Only when they are torn
// down can the list header be destroyed.
if (mList.size() > 0) {
- List< sp<JavaDeathRecipient> >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
(*iter)->warnIfStillLive();
}
}
}
-void DeathRecipientList::add(const sp<JavaDeathRecipient>& recipient) {
+template <typename T>
+void RecipientList<T>::add(const sp<JavaRecipient<T> >& recipient) {
AutoMutex _l(mLock);
- LOGDEATH("DRL @ %p : add JDR %p", this, recipient.get());
+ LOG_DEATH_FREEZE("%s RecipientList @ %p : add JavaRecipient %p", logPrefix<T>(), this,
+ recipient.get());
mList.push_back(recipient);
}
-void DeathRecipientList::remove(const sp<JavaDeathRecipient>& recipient) {
+template <typename T>
+void RecipientList<T>::remove(const sp<JavaRecipient<T> >& recipient) {
AutoMutex _l(mLock);
- List< sp<JavaDeathRecipient> >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
if (*iter == recipient) {
- LOGDEATH("DRL @ %p : remove JDR %p", this, recipient.get());
+ LOG_DEATH_FREEZE("%s RecipientList @ %p : remove JavaRecipient %p", logPrefix<T>(),
+ this, recipient.get());
mList.erase(iter);
return;
}
}
}
-sp<JavaDeathRecipient> DeathRecipientList::find(jobject recipient) {
+template <typename T>
+sp<JavaRecipient<T> > RecipientList<T>::find(jobject recipient) {
AutoMutex _l(mLock);
- List< sp<JavaDeathRecipient> >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
if ((*iter)->matches(recipient)) {
return *iter;
}
@@ -800,10 +910,14 @@
return NULL;
}
-Mutex& DeathRecipientList::lock() {
+template <typename T>
+Mutex& RecipientList<T>::lock() {
return mLock;
}
+using DeathRecipientList = RecipientList<IBinder::DeathRecipient>;
+using FrozenStateChangeCallbackList = RecipientList<IBinder::FrozenStateChangeCallback>;
+
// ----------------------------------------------------------------------------
namespace android {
@@ -821,6 +935,11 @@
// Death recipients for mObject. Reference counted only because DeathRecipients
// hold a weak reference that can be temporarily promoted.
sp<DeathRecipientList> mOrgue; // Death recipients for mObject.
+
+ // Frozen state change callbacks for mObject. Reference counted only because
+ // JavaFrozenStateChangeCallback hold a weak reference that can be
+ // temporarily promoted.
+ sp<FrozenStateChangeCallbackList> mFrozenStateChangCallbackList;
};
BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
@@ -840,12 +959,13 @@
if (val->checkSubclass(&gBinderOffsets)) {
// It's a JavaBBinder created by ibinderForJavaObject. Already has Java object.
jobject object = static_cast<JavaBBinder*>(val.get())->object();
- LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
+ LOG_DEATH_FREEZE("objectForBinder %p: it's our own %p!\n", val.get(), object);
return object;
}
BinderProxyNativeData* nativeData = new BinderProxyNativeData();
nativeData->mOrgue = new DeathRecipientList;
+ nativeData->mFrozenStateChangCallbackList = new FrozenStateChangeCallbackList;
nativeData->mObject = val;
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
@@ -1032,60 +1152,60 @@
// ----------------------------------------------------------------------------
-static jint android_os_Binder_getCallingPid()
+static jint android_os_Binder_getCallingPid(CRITICAL_JNI_PARAMS)
{
return IPCThreadState::self()->getCallingPid();
}
-static jint android_os_Binder_getCallingUid()
+static jint android_os_Binder_getCallingUid(CRITICAL_JNI_PARAMS)
{
return IPCThreadState::self()->getCallingUid();
}
-static jboolean android_os_Binder_isDirectlyHandlingTransactionNative() {
+static jboolean android_os_Binder_isDirectlyHandlingTransactionNative(CRITICAL_JNI_PARAMS) {
return getCurrentServingCall() == BinderCallType::BINDER;
}
-static jlong android_os_Binder_clearCallingIdentity()
+static jlong android_os_Binder_clearCallingIdentity(CRITICAL_JNI_PARAMS)
{
return IPCThreadState::self()->clearCallingIdentity();
}
-static void android_os_Binder_restoreCallingIdentity(jlong token)
+static void android_os_Binder_restoreCallingIdentity(CRITICAL_JNI_PARAMS_COMMA jlong token)
{
IPCThreadState::self()->restoreCallingIdentity(token);
}
-static jboolean android_os_Binder_hasExplicitIdentity() {
+static jboolean android_os_Binder_hasExplicitIdentity(CRITICAL_JNI_PARAMS) {
return IPCThreadState::self()->hasExplicitIdentity();
}
-static void android_os_Binder_setThreadStrictModePolicy(jint policyMask)
+static void android_os_Binder_setThreadStrictModePolicy(CRITICAL_JNI_PARAMS_COMMA jint policyMask)
{
IPCThreadState::self()->setStrictModePolicy(policyMask);
}
-static jint android_os_Binder_getThreadStrictModePolicy()
+static jint android_os_Binder_getThreadStrictModePolicy(CRITICAL_JNI_PARAMS)
{
return IPCThreadState::self()->getStrictModePolicy();
}
-static jlong android_os_Binder_setCallingWorkSourceUid(jint workSource)
+static jlong android_os_Binder_setCallingWorkSourceUid(CRITICAL_JNI_PARAMS_COMMA jint workSource)
{
return IPCThreadState::self()->setCallingWorkSourceUid(workSource);
}
-static jlong android_os_Binder_getCallingWorkSourceUid()
+static jlong android_os_Binder_getCallingWorkSourceUid(CRITICAL_JNI_PARAMS)
{
return IPCThreadState::self()->getCallingWorkSourceUid();
}
-static jlong android_os_Binder_clearCallingWorkSource()
+static jlong android_os_Binder_clearCallingWorkSource(CRITICAL_JNI_PARAMS)
{
return IPCThreadState::self()->clearCallingWorkSource();
}
-static void android_os_Binder_restoreCallingWorkSource(jlong token)
+static void android_os_Binder_restoreCallingWorkSource(CRITICAL_JNI_PARAMS_COMMA jlong token)
{
IPCThreadState::self()->restoreCallingWorkSource(token);
}
@@ -1448,7 +1568,7 @@
BinderProxyNativeData *nd = getBPNativeData(env, obj);
IBinder* target = nd->mObject.get();
- LOGDEATH("linkToDeath: binder=%p recipient=%p\n", target, recipient);
+ LOG_DEATH_FREEZE("linkToDeath: binder=%p recipient=%p\n", target, recipient);
if (!target->localBinder()) {
DeathRecipientList* list = nd->mOrgue.get();
@@ -1479,15 +1599,15 @@
return JNI_FALSE;
}
- LOGDEATH("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
+ LOG_DEATH_FREEZE("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
if (!target->localBinder()) {
status_t err = NAME_NOT_FOUND;
// If we find the matching recipient, proceed to unlink using that
DeathRecipientList* list = nd->mOrgue.get();
- sp<JavaDeathRecipient> origJDR = list->find(recipient);
- LOGDEATH(" unlink found list %p and JDR %p", list, origJDR.get());
+ sp<JavaRecipient<IBinder::DeathRecipient> > origJDR = list->find(recipient);
+ LOG_DEATH_FREEZE(" unlink found list %p and JDR %p", list, origJDR.get());
if (origJDR != NULL) {
wp<IBinder::DeathRecipient> dr;
err = target->unlinkToDeath(origJDR, NULL, flags, &dr);
@@ -1513,11 +1633,85 @@
return res;
}
+static void android_os_BinderProxy_addFrozenStateChangeCallback(
+ JNIEnv* env, jobject obj,
+ jobject callback) // throws RemoteException
+{
+ if (callback == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return;
+ }
+
+ BinderProxyNativeData* nd = getBPNativeData(env, obj);
+ IBinder* target = nd->mObject.get();
+
+ LOG_DEATH_FREEZE("addFrozenStateChangeCallback: binder=%p callback=%p\n", target, callback);
+
+ if (!target->localBinder()) {
+ FrozenStateChangeCallbackList* list = nd->mFrozenStateChangCallbackList.get();
+ auto jfscc = sp<JavaFrozenStateChangeCallback>::make(env, callback, list);
+ status_t err = target->addFrozenStateChangeCallback(jfscc);
+ if (err != NO_ERROR) {
+ // Failure adding the callback, so clear its reference now.
+ jfscc->clearReference();
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
+ }
+ }
+}
+
+static jboolean android_os_BinderProxy_removeFrozenStateChangeCallback(JNIEnv* env, jobject obj,
+ jobject callback) {
+ jboolean res = JNI_FALSE;
+ if (callback == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return res;
+ }
+
+ BinderProxyNativeData* nd = getBPNativeData(env, obj);
+ IBinder* target = nd->mObject.get();
+ if (target == NULL) {
+ ALOGW("Binder has been finalized when calling removeFrozenStateChangeCallback() with "
+ "callback=%p)\n",
+ callback);
+ return JNI_FALSE;
+ }
+
+ LOG_DEATH_FREEZE("removeFrozenStateChangeCallback: binder=%p callback=%p\n", target, callback);
+
+ if (!target->localBinder()) {
+ status_t err = NAME_NOT_FOUND;
+
+ // If we find the matching callback, proceed to unlink using that
+ FrozenStateChangeCallbackList* list = nd->mFrozenStateChangCallbackList.get();
+ sp<JavaRecipient<IBinder::FrozenStateChangeCallback> > origJFSCC = list->find(callback);
+ LOG_DEATH_FREEZE(" removeFrozenStateChangeCallback found list %p and JFSCC %p", list,
+ origJFSCC.get());
+ if (origJFSCC != NULL) {
+ err = target->removeFrozenStateChangeCallback(origJFSCC);
+ if (err == NO_ERROR) {
+ origJFSCC->clearReference();
+ }
+ }
+
+ if (err == NO_ERROR || err == DEAD_OBJECT) {
+ res = JNI_TRUE;
+ } else {
+ jniThrowException(env, "java/util/NoSuchElementException",
+ base::StringPrintf("Frozen state change callback does not exist (%s)",
+ statusToString(err).c_str())
+ .c_str());
+ }
+ }
+
+ return res;
+}
+
static void BinderProxy_destroy(void* rawNativeData)
{
BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
- LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
- nativeData->mObject.get(), nativeData->mOrgue.get());
+ LOG_DEATH_FREEZE("Destroying BinderProxy: binder=%p drl=%p fsccl=%p\n",
+ nativeData->mObject.get(), nativeData->mOrgue.get(),
+ nativeData->mFrozenStateChangCallbackList.get());
delete nativeData;
IPCThreadState::self()->flushCommands();
}
@@ -1552,6 +1746,10 @@
{"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
{"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
{"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+ {"addFrozenStateChangeCallbackNative",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;)V", (void*)android_os_BinderProxy_addFrozenStateChangeCallback},
+ {"removeFrozenStateChangeCallbackNative",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;)Z", (void*)android_os_BinderProxy_removeFrozenStateChangeCallback},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
{"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
@@ -1574,6 +1772,10 @@
gBinderProxyOffsets.mSendDeathNotice =
GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
+ gBinderProxyOffsets.mInvokeFrozenStateChangeCallback =
+ GetStaticMethodIDOrDie(env, clazz, "invokeFrozenStateChangeCallback",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;Landroid/os/"
+ "IBinder;I)V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/jni/android_view_TunnelModeEnabledListener.cpp b/core/jni/android_view_TunnelModeEnabledListener.cpp
index af7bae8..d9ab957 100644
--- a/core/jni/android_view_TunnelModeEnabledListener.cpp
+++ b/core/jni/android_view_TunnelModeEnabledListener.cpp
@@ -88,20 +88,19 @@
void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr) {
sp<TunnelModeEnabledListener> listener = reinterpret_cast<TunnelModeEnabledListener*>(ptr);
- if (SurfaceComposerClient::addTunnelModeEnabledListener(listener) != OK) {
- constexpr auto error_msg = "Couldn't addTunnelModeEnabledListener";
- ALOGE(error_msg);
- jniThrowRuntimeException(env, error_msg);
+ status_t status = SurfaceComposerClient::addTunnelModeEnabledListener(listener);
+ if (status != OK) {
+ ALOGE("Couldn't addTunnelModeEnabledListener (%d)", status);
+ jniThrowRuntimeException(env, "Couldn't addTunnelModeEnabledListener");
}
}
void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
sp<TunnelModeEnabledListener> listener = reinterpret_cast<TunnelModeEnabledListener*>(ptr);
-
- if (SurfaceComposerClient::removeTunnelModeEnabledListener(listener) != OK) {
- constexpr auto error_msg = "Couldn't removeTunnelModeEnabledListener";
- ALOGE(error_msg);
- jniThrowRuntimeException(env, error_msg);
+ status_t status = SurfaceComposerClient::removeTunnelModeEnabledListener(listener);
+ if (status != OK) {
+ ALOGE("Couldn't removeTunnelModeEnabledListener (%d)", status);
+ jniThrowRuntimeException(env, "Couldn't removeTunnelModeEnabledListener");
}
}
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index fba0d81..7ad18b8 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "NativeLibraryHelper"
//#define LOG_NDEBUG 0
+#include <android-base/properties.h>
#include <androidfw/ApkParsing.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
@@ -36,6 +37,7 @@
#include <zlib.h>
#include <memory>
+#include <string>
#include "com_android_internal_content_FileSystemUtils.h"
#include "core_jni_helpers.h"
@@ -125,72 +127,10 @@
return INSTALL_SUCCEEDED;
}
-/*
- * Copy the native library if needed.
- *
- * This function assumes the library and path names passed in are considered safe.
- */
-static install_status_t
-copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
-{
- static const size_t kPageSize = getpagesize();
- void** args = reinterpret_cast<void**>(arg);
- jstring* javaNativeLibPath = (jstring*) args[0];
- jboolean extractNativeLibs = *(jboolean*) args[1];
- jboolean debuggable = *(jboolean*) args[2];
-
- ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
-
- uint32_t uncompLen;
- uint32_t when;
- uint32_t crc;
-
- uint16_t method;
- off64_t offset;
- uint16_t extraFieldLength;
- if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
- &extraFieldLength)) {
- ALOGE("Couldn't read zip entry info\n");
- return INSTALL_FAILED_INVALID_APK;
- }
-
- // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
- // easier to use wrap.sh because it only works when it is extracted, see
- // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
- bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;
-
- if (!extractNativeLibs && !forceExtractCurrentFile) {
- // check if library is uncompressed and page-aligned
- if (method != ZipFileRO::kCompressStored) {
- ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
- fileName);
- return INSTALL_FAILED_INVALID_APK;
- }
-
- if (offset % kPageSize != 0) {
- ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
- "from apk.\n", fileName, kPageSize);
- return INSTALL_FAILED_INVALID_APK;
- }
-
-#ifdef ENABLE_PUNCH_HOLES
- // if library is uncompressed, punch hole in it in place
- if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
- ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
- "%" PRIu64 "",
- fileName, zipFile->getZipFileName(), offset);
- }
-
- // if extra field for this zip file is present with some length, possibility is that it is
- // padding added for zip alignment. Punch holes there too.
- if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
- ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
- }
-#endif // ENABLE_PUNCH_HOLES
-
- return INSTALL_SUCCEEDED;
- }
-
+static install_status_t extractNativeLibFromApk(ZipFileRO* zipFile, ZipEntryRO zipEntry,
+ const char* fileName,
+ const std::string nativeLibPath, uint32_t when,
+ uint32_t uncompLen, uint32_t crc) {
// Build local file path
const size_t fileNameLen = strlen(fileName);
char localFileName[nativeLibPath.size() + fileNameLen + 2];
@@ -313,6 +253,88 @@
}
/*
+ * Copy the native library if needed.
+ *
+ * This function assumes the library and path names passed in are considered safe.
+ */
+static install_status_t copyFileIfChanged(JNIEnv* env, void* arg, ZipFileRO* zipFile,
+ ZipEntryRO zipEntry, const char* fileName) {
+ static const size_t kPageSize = getpagesize();
+ void** args = reinterpret_cast<void**>(arg);
+ jstring* javaNativeLibPath = (jstring*)args[0];
+ jboolean extractNativeLibs = *(jboolean*)args[1];
+ jboolean debuggable = *(jboolean*)args[2];
+ jboolean app_compat_16kb = *(jboolean*)args[3];
+ install_status_t ret = INSTALL_SUCCEEDED;
+
+ ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
+
+ uint32_t uncompLen;
+ uint32_t when;
+ uint32_t crc;
+
+ uint16_t method;
+ off64_t offset;
+ uint16_t extraFieldLength;
+ if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
+ &extraFieldLength)) {
+ ALOGE("Couldn't read zip entry info\n");
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
+ // easier to use wrap.sh because it only works when it is extracted, see
+ // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
+ bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;
+
+ if (!extractNativeLibs && !forceExtractCurrentFile) {
+ // check if library is uncompressed and page-aligned
+ if (method != ZipFileRO::kCompressStored) {
+ ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+ fileName);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ if (offset % kPageSize != 0) {
+ // If the library is zip-aligned correctly for 4kb devices and app compat is
+ // enabled, on 16kb devices fallback to extraction
+ if (offset % 0x1000 == 0 && app_compat_16kb) {
+ ALOGI("16kB AppCompat: Library '%s' is not PAGE(%zu)-aligned - falling back to "
+ "extraction from apk\n",
+ fileName, kPageSize);
+ return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(),
+ when, uncompLen, crc);
+ }
+
+ ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
+ "from apk.\n",
+ fileName, kPageSize);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+#ifdef ENABLE_PUNCH_HOLES
+ // if library is uncompressed, punch hole in it in place
+ if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
+ ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
+ "%" PRIu64 "",
+ fileName, zipFile->getZipFileName(), offset);
+ }
+
+ // if extra field for this zip file is present with some length, possibility is that it is
+ // padding added for zip alignment. Punch holes there too.
+ if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
+ ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
+ }
+#endif // ENABLE_PUNCH_HOLES
+
+ return INSTALL_SUCCEEDED;
+ }
+
+ return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(), when,
+ uncompLen, crc);
+}
+
+/*
* An iterator over all shared libraries in a zip file. An entry is
* considered to be a shared library if all of the conditions below are
* satisfied :
@@ -498,12 +520,24 @@
return status;
}
+static inline bool app_compat_16kb_enabled() {
+ static const size_t kPageSize = getpagesize();
+
+ // App compat is only applicable on 16kb-page-size devices.
+ if (kPageSize != 0x4000) {
+ return false;
+ }
+
+ return android::base::GetBoolProperty("bionic.linker.16kb.app_compat.enabled", false);
+}
+
static jint
com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
jboolean extractNativeLibs, jboolean debuggable)
{
- void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable };
+ jboolean app_compat_16kb = app_compat_16kb_enabled();
+ void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable, &app_compat_16kb };
return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable,
copyFileIfChanged, reinterpret_cast<void*>(args));
}
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 020b27e..19f8299 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -391,6 +391,7 @@
} // namespace android
+#ifndef _WIN32
using namespace android;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
@@ -407,3 +408,4 @@
return JNI_VERSION_1_6;
}
+#endif
diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto
index 5892396..47c97b0 100644
--- a/core/proto/android/widget/remoteviews.proto
+++ b/core/proto/android/widget/remoteviews.proto
@@ -54,6 +54,7 @@
optional bool has_draw_instructions = 13;
repeated bytes bitmap_cache = 14;
optional RemoteCollectionCache remote_collection_cache = 15;
+ repeated Action actions = 16;
message RemoteCollectionCache {
message Entry {
@@ -288,6 +289,91 @@
}
}
}
+
+ message Action {
+ oneof action {
+ AttributeReflectionAction attribute_reflection_action = 1;
+ BitmapReflectionAction bitmap_reflection_action = 2;
+ ComplexUnitDimensionReflectionAction complex_unit_dimension_reflection_action = 3;
+ LayoutParamAction layout_param_action = 4;
+ NightModeReflectionAction night_mode_reflection_action = 5;
+ ReflectionAction reflection_action = 6;
+ RemoveFromParentAction remove_from_parent_action = 7;
+ }
+ }
+
+ message AttributeReflectionAction {
+ optional string view_id = 1;
+ optional string method_name = 2;
+ optional int32 parameter_type = 3;
+ optional int32 resource_type = 4;
+ optional string attribute_id = 5;
+ }
+
+ message BitmapReflectionAction {
+ optional string view_id = 1;
+ optional string method_name = 2;
+ optional int32 bitmap_id = 3;
+ }
+
+ message ComplexUnitDimensionReflectionAction {
+ optional string view_id = 1;
+ optional string method_name = 2;
+ optional int32 parameter_type = 3;
+ optional float dimension_value = 4;
+ optional int32 unit = 5;
+ }
+
+ message LayoutParamAction {
+ optional string view_id = 1;
+ optional int32 property = 2;
+ optional int32 layout_value = 3;
+ optional int32 value_type = 4;
+ }
+
+ message NightModeReflectionAction {
+ optional string view_id = 1;
+ optional string method_name = 2;
+ optional int32 parameter_type = 3;
+ oneof light {
+ Icon light_icon = 4;
+ android.content.res.ColorStateListProto light_color_state_list = 5;
+ int32 light_int = 6;
+ }
+ oneof dark {
+ Icon dark_icon = 7;
+ android.content.res.ColorStateListProto dark_color_state_list = 8;
+ int32 dark_int = 9;
+ }
+ }
+
+ message ReflectionAction {
+ optional string view_id = 1;
+ optional string method_name = 2;
+ optional int32 parameter_type = 3;
+ oneof reflection_value {
+ bool boolean_value = 4;
+ bytes byte_value = 5;
+ int32 short_value = 6;
+ int32 int_value = 7;
+ int64 long_value = 8;
+ float float_value = 9;
+ double double_value = 10;
+ int32 char_value = 11;
+ string string_value = 12;
+ CharSequence char_sequence_value = 13;
+ string uri_value = 14;
+ bytes bitmap_value = 15;
+ android.content.res.ColorStateListProto color_state_list_value = 16;
+ Icon icon_value = 17;
+ int32 blend_mode_value = 18;
+ // Intent and Bundle values are excluded.
+ }
+ }
+
+ message RemoveFromParentAction {
+ optional string view_id = 1;
+ }
}
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 17ff2eb..d35c66e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4033,7 +4033,6 @@
<!-- Allows an application to manage policy related to block package uninstallation.
<p>Protection level: internal|role
<p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL"
android:protectionLevel="internal|role" />
@@ -4041,7 +4040,6 @@
<!-- Allows an application to manage policy related to camera toggle.
<p>Protection level: internal|role
<p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE"
android:protectionLevel="internal|role" />
@@ -4049,7 +4047,6 @@
<!-- Allows an application to manage policy related to microphone toggle.
<p>Protection level: internal|role
<p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE"
android:protectionLevel="internal|role" />
@@ -7648,7 +7645,8 @@
<permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
+ <!-- @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY) @SystemApi
+ Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
ensure that only the system can bind to it.
@hide This is not a third-party API (intended for OEMs and system apps).
-->
@@ -8170,7 +8168,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 98de27b..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:pathData="M160,480v-80h320v80L160,480ZM480,880q-80,0 -153.5,-29.5T196,764l56,-56q47,44 106,68t122,24q133,0 226.5,-93.5T800,480q0,-133 -93.5,-226.5T480,160v-80q83,0 155.5,31.5t127,86q54.5,54.5 86,127T880,480q0,82 -31.5,155t-86,127.5q-54.5,54.5 -127,86T480,880Z"
- android:fillColor="@android:color/white"/>
+ <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/layout/autofill_dataset_picker_header_footer.xml b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
index 4d5f4f0..027f530 100644
--- a/core/res/res/layout/autofill_dataset_picker_header_footer.xml
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
@@ -37,6 +37,7 @@
<ListView
android:id="@+id/autofill_dataset_list"
android:layout_weight="1"
+ android:fadeScrollbars="false"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:drawSelectorOnTop="true"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 381111c..a1dea82 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"’n App verberg die toestemmingversoek en jou antwoord kan dus nie geverifieer word nie."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tik op \'n kenmerk om dit te begin gebruik:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Kies kenmerke om saam met die toeganklikheidknoppie te gebruik"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Kies kenmerke om saam met die volumesleutelkortpad te gebruik"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is afgeskakel"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Wysig kortpaaie"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index aa7dcec..2873785 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"አንድ መተግበሪያ የፍቃድ ጥያቄውን እያደበዘዘ ነው ስለዚህ የእርስዎ ምላሽ ሊረጋገጥ አይችልም።"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"አንድ ባህሪን መጠቀም ለመጀመር መታ ያድርጉት፦"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"በተደራሽነት አዝራር የሚጠቀሙባቸው ባሕሪያት ይምረጡ"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"በድምጽ ቁልፍ አቋራጭ የሚጠቀሙባቸው ባሕሪያት ይምረጡ"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ጠፍቷል"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"አቋራጮችን አርትዕ ያድርጉ"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ተከናውኗል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 4f03ff3..7f02576 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1750,7 +1750,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"تعذَّر التحقّق من ردّك بسبب حجب أحد التطبيقات طلب الحصول على الإذن."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"انقر على ميزة لبدء استخدامها:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"اختيار الميزات التي تريد استخدامها مع زر أدوات تمكين الوصول"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"اختيار الميزات التي تريد استخدامها مع اختصار مفتاح التحكّم في مستوى الصوت"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"تم إيقاف <xliff:g id="SERVICE_NAME">%s</xliff:g>."</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"تعديل الاختصارات"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"تم"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 8c9cda9..bc1dba27 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"এটা এপে অনুমতিৰ অনুৰোধটো অস্পষ্ট কৰি আছে আৰু সেয়েহে আপোনাৰ সঁহাৰিটো সত্যাপন কৰিব নোৱাৰি।"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"কোনো এটা সুবিধা ব্যৱহাৰ কৰিবলৈ সেইটোত টিপক:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"সাধ্য-সুবিধা বুটামটোৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ সুবিধাসমূহ বাছনি কৰক"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ভলিউম কীৰ শ্বৰ্টকাটটোৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ সুবিধাসমূহ বাছনি কৰক"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> বন্ধ কৰা হৈছে"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"শ্বৰ্টকাটসমূহ সম্পাদনা কৰক"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"কৰা হ’ল"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index afbe715..e93a57b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Bir tətbiq icazə sorğusunu gizlətdiyi üçün cavabı yoxlamaq mümkün deyil."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Funksiyanı istifadə etmək üçün onun üzərinə toxunun:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Xüsusi imkanlar düyməsinin köməyilə işə salınacaq funksiyaları seçin"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Səs səviyyəsi düyməsinin qısayolu ilə istifadə edəcəyiniz funksiyaları seçin"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> deaktiv edilib"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Qısayolları redaktə edin"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hazırdır"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index a20775d..b83eca5 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacija krije zahtev za dozvolu, pa odgovor ne može da se verifikuje."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite neku funkciju da biste počeli da je koristite:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odaberite funkcije koje ćete koristiti sa dugmetom Pristupačnost"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije za prečicu tasterom jačine zvuka"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usluga <xliff:g id="SERVICE_NAME">%s</xliff:g> je isključena"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Izmenite prečice"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gotovo"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 871f8d4..59645706 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1748,7 +1748,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Праграма хавае запыт дазволу, таму ваш адказ немагчыма спраўдзіць."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Каб пачаць выкарыстоўваць функцыю, націсніце на яе:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Выберыце функцыі, якія будзеце выкарыстоўваць з кнопкай спецыяльных магчымасцей"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберыце функцыі для выкарыстання з клавішай гучнасці"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сэрвіс \"<xliff:g id="SERVICE_NAME">%s</xliff:g>\" выключаны"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Змяніць ярлыкі"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Гатова"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index c22325c..b6fad9e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Отговорът ви не може да бъде потвърден, тъй като приложение прикрива заявката за разрешение."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Докоснете дадена функция, за да започнете да я използвате:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Избиране на функции, които да използвате с бутона за достъпност"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Избиране на функции, които да използвате с прекия път чрез бутона за силата на звука"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Изключихте <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Редактиране на преките пътища"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 776714c..c434fe9 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"কোনও অ্যাপ অনুমতির অনুরোধ আড়াল করছে তাই আপনার উত্তর যাচাই করা যাবে না।"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"কোনও ফিচার ব্যবহার করা শুরু করতে, সেটিতে ট্যাপ করুন:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"অ্যাক্সেসিবিলিটি বোতামের সাহায্যে আপনি যেসব ফিচার ব্যবহার করতে চান সেগুলি বেছে নিন"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ভলিউম কী শর্টকাটের সাহায্যে আপনি যেসব ফিচার ব্যবহার করতে চান সেগুলি বেছে নিন"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> বন্ধ করে দেওয়া হয়েছে"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"শর্টকাট এডিট করুন"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"হয়ে গেছে"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 9306cfd..91c7701 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacija skriva zahtjev za odobrenje, pa se vaš odgovor ne može potvrditi."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite funkciju da je počnete koristiti:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odaberite funkcije koje ćete koristiti s dugmetom Pristupačnost"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije koje ćete koristiti pomoću prečice tipke za jačinu zvuka"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usluga <xliff:g id="SERVICE_NAME">%s</xliff:g> je isključena"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Uredi prečice"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gotovo"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 80420dd..cbe9c39 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Una aplicació està ocultant la sol·licitud de permís, de manera que la teva resposta no es pot verificar"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toca una funció per començar a utilitzar-la:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Tria les funcions que vols utilitzar amb el botó d\'accessibilitat"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Tria les funcions que vols utilitzar amb la drecera per a tecles de volum"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> s\'ha desactivat"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edita les dreceres"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Fet"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index bc5cdfe..b9ceede 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1748,7 +1748,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Žádost o oprávnění skrývá nějaká aplikace, proto vaši odpověď nelze ověřit."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Chcete-li některou funkci začít používat, klepněte na ni:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Vyberte funkce, které budete používat s tlačítkem přístupnosti"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Vyberte funkce, které budete používat se zkratkou tlačítka hlasitosti"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Služba <xliff:g id="SERVICE_NAME">%s</xliff:g> byla vypnuta"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Upravit zkratky"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hotovo"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 6cbe673..d66ebd90 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"En app skjuler anmodningen om tilladelse, så dit svar kan ikke verificeres."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tryk på en funktion for at bruge den:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Vælg, hvilke funktioner du vil bruge med knappen til hjælpefunktioner"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Vælg de funktioner, du vil bruge via lydstyrkeknapperne"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> er blevet deaktiveret"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Rediger genveje"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Udfør"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index dece83f..3879767 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Die Berechtigungsanfrage wird durch eine andere App verdeckt. Daher kann deine Antwort nicht geprüft werden."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Zum Auswählen der gewünschten Funktion tippen:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Funktionen auswählen, die du mit der Schaltfläche \"Bedienungshilfen\" verwenden möchtest"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Funktionen für Verknüpfung mit Lautstärketaste auswählen"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> wurde deaktiviert"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Kurzbefehle bearbeiten"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Fertig"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index cc179a4..28a5e55 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Μια εφαρμογή αποκρύπτει το αίτημα άδειας, με αποτέλεσμα να μην είναι δυνατή η επαλήθευση της απάντησής σας."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Πατήστε μια λειτουργία για να ξεκινήσετε να τη χρησιμοποιείτε:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Επιλέξτε τις λειτουργίες που θέλετε να χρησιμοποιείτε με το κουμπί προσβασιμότητας."</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Επιλέξτε τις λειτουργίες που θέλετε να χρησιμοποιείτε με τη συντόμευση κουμπιού έντασης ήχου"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Η υπηρεσία <xliff:g id="SERVICE_NAME">%s</xliff:g> έχει απενεργοποιηθεί."</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Επεξεργασία συντομεύσεων"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Τέλος"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index caa52c4..70d86e7 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 3850b00..19547f8 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the accessibility button"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index bf5c61c..d3f0c64 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index e829fa3..7c3be15 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 0b9be3b..c1eade1 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the accessibility button"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3814944..773edd3 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Una app está cubriendo la solicitud de permiso, por lo que no se puede verificar tu respuesta."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Presiona una función para comenzar a usarla:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Selecciona las funciones a utilizar con el botón de accesibilidad"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Selecciona las funciones a usar con las teclas de volumen"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Se desactivó <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar accesos directos"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Listo"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 215cf39..ff7d912 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Una aplicación está ocultando la solicitud de permiso, por lo que no se puede verificar tu respuesta."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toca una función para empezar a usarla:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Selecciona qué funciones usar con el botón de accesibilidad"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Selecciona qué funciones usar con la tecla de volumen"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Se ha desactivado <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar accesos directos"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hecho"</string>
diff --git a/core/res/res/values-et-rEE/config.xml b/core/res/res/values-et-rEE/config.xml
new file mode 100644
index 0000000..cf4d07f2
--- /dev/null
+++ b/core/res/res/values-et-rEE/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<resources>
+ <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4694efa..f313fb2 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Rakendus varjab loataotlust, nii et teie vastust ei saa kinnitada."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Puudutage funktsiooni, et selle kasutamist alustada."</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Valige funktsioonid, mida juurdepääsetavuse nupuga kasutada"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Valige helitugevuse nupu otsetee funktsioonid"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> on välja lülitatud"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Muuda otseteid"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Valmis"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 2cae18a..b0775d0 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -252,7 +252,7 @@
<string name="shutdown_confirm_question" msgid="796151167261608447">"Itzali egin nahi duzu?"</string>
<string name="reboot_safemode_title" msgid="5853949122655346734">"Berrabiarazi modu seguruan"</string>
<string name="reboot_safemode_confirm" msgid="1658357874737219624">"Modu seguruan berrabiarazi nahi duzu? Instalatutako hirugarrenen aplikazioak desgaituko dira. Berriro berrabiarazi ondoren leheneratuko dira."</string>
- <string name="recent_tasks_title" msgid="8183172372995396653">"Azkenak"</string>
+ <string name="recent_tasks_title" msgid="8183172372995396653">"Azkenaldikoak"</string>
<string name="no_recent_tasks" msgid="9063946524312275906">"Ez dago azkenaldian erabilitako aplikaziorik."</string>
<string name="global_actions" product="tablet" msgid="4412132498517933867">"Tabletaren aukerak"</string>
<string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV gailuaren aukerak"</string>
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikazio bat baimen-eskaera oztopatzen ari da eta, ondorioz, ezin da egiaztatu erantzuna."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Eginbide bat erabiltzen hasteko, saka ezazu:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoiarekin"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Aukeratu zein eginbide erabili nahi duzun bolumen-botoien lasterbidearekin"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Desaktibatu da <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editatu lasterbideak"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Eginda"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 9a75d3a..5e474cd 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1183,7 +1183,7 @@
<string name="copyUrl" msgid="6229645005987260230">"کپی URL"</string>
<string name="selectTextMode" msgid="3225108910999318778">"انتخاب متن"</string>
<string name="undo" msgid="3175318090002654673">"لغو"</string>
- <string name="redo" msgid="7231448494008532233">"بازانجام"</string>
+ <string name="redo" msgid="7231448494008532233">"ازنو انجام دادن"</string>
<string name="autofill" msgid="511224882647795296">"تکمیل خودکار"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"انتخاب متن"</string>
<string name="addToDictionary" msgid="8041821113480950096">"افزودن به واژهنامه"</string>
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"پاسخ شما تأیید نشد زیرا یک برنامه درخواست اجازه را مسدود کرده است."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"برای استفاده از ویژگی، روی آن تکضرب بزنید:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"انتخاب ویژگیهای موردنظر برای استفاده با دکمه دسترسپذیری"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"انتخاب ویژگیهای موردنظر برای استفاده با میانبر کلید میزان صدا"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> خاموش شده است"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ویرایش میانبرها"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"تمام"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 1b2ddb0..dec5b64 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Sovellus peittää lupapyynnön, joten vastaustasi ei voi vahvistaa."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Aloita ominaisuuden käyttö napauttamalla sitä:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Valitse ominaisuudet, joita käytetään esteettömyyspainikkeella"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Valitse ominaisuudet, joita käytetään äänenvoimakkuuspikanäppäimellä"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> on laitettu pois päältä"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Muokkaa pikakuvakkeita"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Valmis"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index d3ebe5a..7cb9e06 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Une appli masque la demande d\'autorisation de sorte que votre réponse ne peut pas être vérifiée."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toucher une fonctionnalité pour commencer à l\'utiliser :"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choisir les fonctionnalités à utiliser à l\'aide du bouton d\'accessibilité"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choisir les fonctionnalités à utiliser avec le raccourci des touches de volume"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> a été désactivé"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Modifier les raccourcis"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"OK"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index d617143..ca3998c 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Une application masque la demande d\'autorisation. Votre réponse ne peut donc pas être vérifiée."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Appuyez sur une fonctionnalité pour commencer à l\'utiliser :"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choisir les fonctionnalités à utiliser avec le bouton Accessibilité"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choisir les fonctionnalités à utiliser avec le raccourci des touches de volume"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Le service <xliff:g id="SERVICE_NAME">%s</xliff:g> a été désactivé"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Modifier les raccourcis"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"OK"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 9366f4e..e07218c 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Hai unha aplicación que está ocultando a solicitude de permiso, polo que non se pode verificar a túa resposta."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tocar unha función para comezar a utilizala:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escoller as funcións que queres utilizar co botón Accesibilidade"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolle as funcións que queres utilizar co atallo da tecla de volume"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g>: desactivouse"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar atallos"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Feito"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6312704..2810b5e 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -266,7 +266,7 @@
<string name="global_action_logout" msgid="6093581310002476511">"સત્ર સમાપ્ત કરો"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"સ્ક્રીનશૉટ"</string>
<string name="bugreport_title" msgid="8549990811777373050">"બગ રિપોર્ટ"</string>
- <string name="bugreport_message" msgid="5212529146119624326">"આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
+ <string name="bugreport_message" msgid="5212529146119624326">"આ, એક ઇ-મેઇલ મેસેજ તરીકે મોકલવા માટે, તમારા વર્તમાન ડિવાઇસના સ્ટેટસ વિશે માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટના શરુ થવાથી લઈને મોકલવા માટે તૈયાર થવા સુધીની પ્રક્રિયામાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"ક્રિયાપ્રતિક્રિયાત્મક રિપોર્ટ"</string>
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"મોટાભાગના સંજોગોમાં આનો ઉપયોગ કરો. તે રિપોર્ટની પ્રગતિને ટ્રૅક કરવા, સમસ્યા વિશે વધુ વિગતો દાખલ કરવાની અને સ્ક્રીનશૉટ્સ લેવાની મંજૂરી આપે છે. તે કેટલાક ઓછા ઉપયોગમાં આવતાં વિભાગો કે જે જાણ કરવામાં વધુ સમય લેતાં હોય તેને છોડી દઈ શકે છે."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"પૂર્ણ રિપોર્ટ"</string>
@@ -291,7 +291,7 @@
<string name="notification_channel_security" msgid="8516754650348238057">"સુરક્ષા"</string>
<string name="notification_channel_car_mode" msgid="2123919247040988436">"કાર મોડ"</string>
<string name="notification_channel_account" msgid="6436294521740148173">"એકાઉન્ટ સ્થિતિ"</string>
- <string name="notification_channel_developer" msgid="1691059964407549150">"વિકાસકર્તા માટેના સંદેશા"</string>
+ <string name="notification_channel_developer" msgid="1691059964407549150">"ડેવલપર માટેના મેસેજ"</string>
<string name="notification_channel_developer_important" msgid="7197281908918789589">"ડેવલપર માટેના મહત્ત્વપૂર્ણ મેસેજ"</string>
<string name="notification_channel_updates" msgid="7907863984825495278">"અપડેટ્સ"</string>
<string name="notification_channel_network_status" msgid="2127687368725272809">"નેટવર્ક સ્થિતિ"</string>
@@ -324,7 +324,7 @@
<string name="permgrouplab_calendar" msgid="6426860926123033230">"કૅલેન્ડર"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS મેસેજ મોકલવાની અને જોવાની"</string>
+ <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS મેસેજ મોકલો અને જુઓ"</string>
<string name="permgrouplab_storage" msgid="17339216290379241">"ફાઇલો"</string>
<string name="permgroupdesc_storage" msgid="5378659041354582769">"તમારા ડિવાઇસ પરની ફાઇલો ઍક્સેસ કરો"</string>
<string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"મ્યુઝિક અને ઑડિયો"</string>
@@ -400,7 +400,7 @@
<string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."</string>
<string name="permdesc_readSms" product="default" msgid="774753371111699782">"આ ઍપ, તમારા ફોન પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."</string>
<string name="permlab_receiveWapPush" msgid="4223747702856929056">"ટેક્સ્ટ મેસેજ (WAP) મેળવો"</string>
- <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"એપ્લિકેશનને WAP સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આ પરવાનગીમાં તમને દર્શાવ્યા વિના તમને મોકલેલ સંદેશાઓનું નિરીક્ષણ કરવાની અને કાઢી નાખવાની ક્ષમતાનો સમાવેશ થાય છે."</string>
+ <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"ઍપને WAP મેસેજ મેળવવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આ પરવાનગીમાં તમને દર્શાવ્યા વિના તમને મોકલેલા મેસેજનું નિરીક્ષણ કરવાની અને ડિલીટ કરવાની ક્ષમતાનો સમાવેશ થાય છે."</string>
<string name="permlab_getTasks" msgid="7460048811831750262">"ચાલુ ઍપ્લિકેશનો પુનઃપ્રાપ્ત કરો"</string>
<string name="permdesc_getTasks" msgid="7388138607018233726">"એપ્લિકેશનને વર્તમાનમાં અને તાજેતરમાં ચાલી રહેલ Tasks વિશેની વિગતવાર માહિતી પુનઃપ્રાપ્ત કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને ઉપકરણ પર કઈ એપ્લિકેશન્સનો ઉપયોગ થાય છે તેના વિશેની માહિતી શોધવાની મંજૂરી આપી શકે છે."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"પ્રોફાઇલ અને ડિવાઇસ માલિકોને મેનેજ કરો"</string>
@@ -1363,7 +1363,7 @@
<string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> મોટા પ્રમાણમાં SMS મેસેજ મોકલી રહ્યું છે. શું તમે મેસેજ મોકલવાનું ચાલુ રાખવા માટે આ એપને મંજૂરી આપવા માગો છો?"</string>
<string name="sms_control_yes" msgid="4858845109269524622">"મંજૂરી આપો"</string>
<string name="sms_control_no" msgid="4845717880040355570">"નકારો"</string>
- <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> તમને <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> પર સંદેશ મોકલવા માગે છે."</string>
+ <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>, <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> પર મેસેજ મોકલવા માગે છે."</string>
<string name="sms_short_code_details" msgid="2723725738333388351">"આનાથી તમારા મોબાઇલ એકાઉન્ટ પર "<b>"શુલ્ક લાગી શકે છે"</b>"."</string>
<string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"આનાથી તમારા મોબાઇલ એકાઉન્ટ પર શુલ્ક લાગશે."</b></string>
<string name="sms_short_code_confirm_allow" msgid="920477594325526691">"મોકલો"</string>
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"કોઈ ઍપ પરવાનગીની વિનંતીને ઢાંકી રહી છે, તેથી તમારા પ્રતિસાદની ચકાસણી કરી શકાતી નથી."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"સુવિધાનો ઉપયોગ શરૂ કરવા તેના પર ટૅપ કરો:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ઍક્સેસિબિલિટી બટન વડે તમે ઉપયોગમાં લેવા માગો છો તે સુવિધાઓ પસંદ કરો"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"વૉલ્યૂમ કી શૉર્ટકટ વડે તમે ઉપયોગમાં લેવા માગો છો તે સુવિધાઓ પસંદ કરો"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> બંધ કરવામાં આવ્યું છે"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"શૉર્ટકટમાં ફેરફાર કરો"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"થઈ ગયું"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 02369f9..700e91a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ऐप्लिकेशन की वजह से, अनुमति का अनुरोध समझने में परेशानी हो रही है. इसलिए, आपके जवाब की पुष्टि नहीं की जा सकी."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"किसी सुविधा का इस्तेमाल करने के लिए, उस पर टैप करें:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"सुलभता बटन पर टैप करके, इस्तेमाल करने के लिए सुविधाएं चुनें"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"चुनें कि आवाज़ कम या ज़्यादा करने वाले बटन के शॉर्टकट से आप किन सुविधाओं का इस्तेमाल करना चाहते हैं"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> को बंद कर दिया गया है"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"शॉर्टकट में बदलाव करें"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"हो गया"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 487d9e7..2596de2 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacija prekriva upit za dopuštenje pa se vaš odgovor ne može potvrditi."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite značajku da biste je počeli koristiti:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odabir značajki za upotrebu pomoću gumba za Pristupačnost"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odabir značajki za upotrebu pomoću prečaca tipki za glasnoću"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usluga <xliff:g id="SERVICE_NAME">%s</xliff:g> je isključena"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Uredite prečace"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gotovo"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index ca8787d..09af506 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Az egyik alkalmazás eltakarja az engedélykérelmet, így az Ön válasza nem ellenőrizhető."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Koppintson valamelyik funkcióra a használatához:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Kiválaszthatja a Kisegítő lehetőségek gombbal használni kívánt funkciókat"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Kiválaszthatja a hangerőgombbal használni kívánt funkciókat"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kikapcsolva"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Gyorsparancsszerkesztés"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Kész"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 273e7745..a28f413 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Հավելվածը թաքցնում է թույլտվության հայտը, ուստի ձեր պատասխանը հնարավոր չէ ստուգել։"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ընտրեք՝ որ գործառույթն օգտագործել"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Ընտրեք գործառույթները, որոնք կբացվեն «Հատուկ գործառույթներ» կոճակի միջոցով"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Ընտրեք գործառույթները, որոնք կբացվեն ձայնի կարգավորման կոճակի միջոցով"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ծառայությունն անջատված է"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Փոփոխել դյուրանցումները"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Պատրաստ է"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 47588ff..76c89ea 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikasi menghalangi permintaan izin sehingga respons Anda tidak dapat diverifikasi."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ketuk fitur untuk mulai menggunakannya:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pilih fitur yang akan digunakan dengan tombol aksesibilitas"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pilih fitur yang akan digunakan dengan pintasan tombol volume"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> telah dinonaktifkan"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit pintasan"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Selesai"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index a833c8d..3eb2e4b 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Forrit er að fela heimildarbeiðnina svo ekki er hægt að staðfesta svarið þitt."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ýttu á eiginleika til að byrja að nota hann:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Veldu eiginleika sem á að nota með aðgengishnappinum"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Veldu eiginleika sem á að nota með flýtileið hljóðstyrkstakka"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Slökkt hefur verið á <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Breyta flýtileiðum"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Lokið"</string>
diff --git a/core/res/res/values-it-feminine/strings.xml b/core/res/res/values-it-feminine/strings.xml
index 141b467..1a69a63 100644
--- a/core/res/res/values-it-feminine/strings.xml
+++ b/core/res/res/values-it-feminine/strings.xml
@@ -20,6 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="relationTypeFriend" msgid="3192092625893980574">"Amica"</string>
<string name="relationTypeSpouse" msgid="6916682664436031703">"Moglie"</string>
</resources>
diff --git a/core/res/res/values-it-masculine/strings.xml b/core/res/res/values-it-masculine/strings.xml
index 7310eb8..86c4408 100644
--- a/core/res/res/values-it-masculine/strings.xml
+++ b/core/res/res/values-it-masculine/strings.xml
@@ -20,6 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="relationTypeFriend" msgid="3192092625893980574">"Amico"</string>
<string name="relationTypeSpouse" msgid="6916682664436031703">"Marito"</string>
</resources>
diff --git a/core/res/res/values-it-neuter/strings.xml b/core/res/res/values-it-neuter/strings.xml
index ce433d7..7c33e91 100644
--- a/core/res/res/values-it-neuter/strings.xml
+++ b/core/res/res/values-it-neuter/strings.xml
@@ -20,6 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="relationTypeFriend" msgid="3192092625893980574">"Amicə"</string>
<string name="relationTypeSpouse" msgid="6916682664436031703">"Coniuge"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 662a9c0..29533d5 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -969,7 +969,7 @@
<string name="relationTypeChild" msgid="9076258911292693601">"Figlio"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Convivente"</string>
<string name="relationTypeFather" msgid="3856225062864790596">"Padre"</string>
- <string name="relationTypeFriend" msgid="3192092625893980574">"Persona amica"</string>
+ <string name="relationTypeFriend" msgid="3192092625893980574">"Amico"</string>
<string name="relationTypeManager" msgid="2272860813153171857">"Dirigente"</string>
<string name="relationTypeMother" msgid="2331762740982699460">"Madre"</string>
<string name="relationTypeParent" msgid="4177920938333039882">"Genitore"</string>
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Un\'app nasconde la tua richiesta di autorizzazione, per cui non abbiamo potuto verificare la tua risposta."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tocca una funzionalità per iniziare a usarla:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Scegli le funzionalità da usare con il pulsante Accessibilità"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Scegli le funzionalità da usare con la scorciatoia dei tasti del volume"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Il servizio <xliff:g id="SERVICE_NAME">%s</xliff:g> è stato disattivato"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Modifica scorciatoie"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Fine"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 37bffa4..52b6173 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"אפליקציה מסתירה את בקשת ההרשאה כך שלא ניתן לאמת את התשובה שלך."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"יש להקיש על תכונה כדי להתחיל להשתמש בה:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"בחירת תכונה לשימוש עם לחצן הנגישות"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"בחירת תכונות לשימוש עם מקש הקיצור לעוצמת הקול"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"שירות <xliff:g id="SERVICE_NAME">%s</xliff:g> כבוי"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"עריכת קיצורי הדרך"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"סיום"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index cce4abe..bcff813 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"権限のリクエストを遮っているアプリがあるため、同意の回答を確認できません。"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"使用を開始する機能をタップ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ユーザー補助機能ボタンで使用する機能の選択"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"音量ボタンのショートカットで使用する機能の選択"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> はオフになっています"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ショートカットの編集"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"完了"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 0d8b047..0adebb0 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"აპი მალავს ნებართვის მოთხოვნას, ასე რომ, თქვენი პასუხი ვერ დადასტურდება."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"შეეხეთ ფუნქციას მისი გამოყენების დასაწყებად:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"აირჩიეთ ფუნქციები, რომელთა გამოყენებაც გსურთ მარტივი წვდომის ღილაკით"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"აირჩიეთ ფუნქციები, რომელთა გამოყენებაც გსურთ ხმის ღილაკის მალსახმობით"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> გამორთულია"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"მალსახმობების რედაქტირება"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"მზადაა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 72a2a3b..d9ee240 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Қолданба рұқсат сұрауын жасырып тұрғандықтан, жауабыңыз расталмайды."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Функцияны пайдалана бастау үшін түртіңіз:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"\"Арнайы мүмкіндіктер\" түймесімен қолданылатын функцияларды таңдаңыз"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дыбыс деңгейі пернелері тіркесімімен қолданылатын функцияларды таңдаңыз"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> қызметі өшірулі."</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Жылдам пәрмендерді өзгерту"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Дайын"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 2e5589e..f3dd775 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"កម្មវិធីមួយកំពុងបិទបាំងសំណើសុំការអនុញ្ញាត ដូច្នេះមិនអាចផ្ទៀងផ្ទាត់ការឆ្លើយតបរបស់អ្នកបានទេ។"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ចុចមុខងារណាមួយ ដើម្បចាប់ផ្ដើមប្រើ៖"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ជ្រើសរើសមុខងារ ដើម្បីប្រើជាមួយប៊ូតុងភាពងាយស្រួល"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ជ្រើសរើសមុខងារ ដើម្បីប្រើជាមួយផ្លូវកាត់គ្រាប់ចុចកម្រិតសំឡេង"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"បានបិទ <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"កែផ្លូវកាត់"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"រួចរាល់"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index e5a5092..a772e32 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1403,7 +1403,7 @@
<string name="usb_accessory_notification_title" msgid="1385394660861956980">"USB ಪರಿಕರವನ್ನು ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
<string name="usb_notification_message" msgid="4715163067192110676">"ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="usb_power_notification_message" msgid="7284765627437897702">"ಸಂಪರ್ಕಗೊಂಡಿರುವ ಸಾಧನವನ್ನು ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ. ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"ಅನ್ಲಾಗ್ ಆಡಿಯೋ ಪರಿಕರ ಪತ್ತೆಯಾಗಿದೆ"</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"ಅನ್ಲಾಗ್ ಆಡಿಯೋ ಆ್ಯಕ್ಸೆಸರಿ ಪತ್ತೆಯಾಗಿದೆ"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ಲಗತ್ತಿಸಲಾದ ಸಾಧನವು ಈ ಫೋನಿನೊಂದಿಗೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
<string name="adb_active_notification_message" msgid="5617264033476778211">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ಆ್ಯಪ್ವೊಂದು ಅನುಮತಿ ವಿನಂತಿಯನ್ನು ಮರೆಮಾಚುತ್ತಿರುವ ಕಾರಣ ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ವೈಶಿಷ್ಟ್ದ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ಅದನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಬಟನ್ ಜೊತೆಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ವಾಲ್ಯೂಮ್ ಕೀ ಶಾರ್ಟ್ಕಟ್ ಜೊತೆಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
@@ -2152,7 +2153,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"ಸರಿ"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"ಆಫ್ ಮಾಡಿ"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"ವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್ಗಳು Android 12 ರಲ್ಲಿ Android ಅಡಾಪ್ಟಿವ್ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಬದಲಾಯಿಸಿವೆ. ಈ ವೈಶಿಷ್ಟ್ಯವು ಸೂಚಿಸಿದ ಕ್ರಿಯೆಗಳು ಮತ್ತು ಪ್ರತ್ಯುತ್ತರಗಳನ್ನು ತೋರಿಸುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಯೋಜಿಸುತ್ತದೆ.\n\nವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್ಗಳು ಸಂಪರ್ಕ ಹೆಸರುಗಳು ಮತ್ತು ಸಂದೇಶಗಳಂತಹ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆ ವಿಷಯವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ಈ ವೈಶಿಷ್ಟ್ಯವು ಫೋನ್ ಕರೆಗಳಿಗೆ ಉತ್ತರಿಸುವುದು ಮತ್ತು \'ಅಡಚಣೆ ಮಾಡಬೇಡಿ\' ಅನ್ನು ನಿಯಂತ್ರಿಸುವಂತಹ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ವಜಾಗೊಳಿಸಬಹುದು ಅಥವಾ ಪ್ರತಿಕ್ರಿಯಿಸಬಹುದು."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"ವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್ಗಳು Android 12 ರಲ್ಲಿ Android ಅಡಾಪ್ಟಿವ್ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಬದಲಾಯಿಸಿವೆ. ಈ ವೈಶಿಷ್ಟ್ಯವು ಸೂಚಿಸಿದ ಕ್ರಿಯೆಗಳು ಮತ್ತು ಪ್ರತ್ಯುತ್ತರಗಳನ್ನು ತೋರಿಸುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಆಯೋಜಿಸುತ್ತದೆ.\n\nವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್ಗಳು ಸಂಪರ್ಕ ಹೆಸರುಗಳು ಮತ್ತು ಸಂದೇಶಗಳಂತಹ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ನೋಟಿಫಿಕೇಶನ್ ವಿಷಯವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ಈ ವೈಶಿಷ್ಟ್ಯವು ಫೋನ್ ಕರೆಗಳಿಗೆ ಉತ್ತರಿಸುವುದು ಮತ್ತು \'ಅಡಚಣೆ ಮಾಡಬೇಡಿ\' ಅನ್ನು ನಿಯಂತ್ರಿಸುವಂತಹ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ವಜಾಗೊಳಿಸಬಹುದು ಅಥವಾ ಪ್ರತಿಕ್ರಿಯಿಸಬಹುದು."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ದೈನಂದಿನ ಸ್ಥಿತಿಯ ಮಾಹಿತಿಯ ನೋಟಿಫಿಕೇಶನ್"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯನ್ನು ವಿಸ್ತರಿಸಲು ಬ್ಯಾಟರಿ ಬಳಕೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 1413170..0e47e59 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"앱에서 권한 요청을 가려서 응답을 확인할 수 없습니다."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"기능을 사용하려면 탭하세요"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"접근성 버튼으로 사용할 기능 선택"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"볼륨 키 단축키로 사용할 기능 선택"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g>이(가) 사용 중지됨"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"단축키 수정"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"완료"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 112bf0a..4c2ee5f 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Колдонмо уруксат суроону жашырып койгондуктан, жообуңузду ырастоо мүмкүн эмес."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Функцияны колдонуп баштоо үчүн аны таптап коюңуз:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Атайын мүмкүнчүлүктөр баскычы менен колдонгуңуз келген функцияларды тандаңыз"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Үндү катуулатуу/акырындатуу баскычтары менен кайсы функцияларды иштеткиңиз келет?"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> өчүрүлдү"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Кыска жолдорду түзөтүү"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Бүттү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index ba43481..b627503 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ແອັບໜຶ່ງກຳລັງປິດບັງຄຳຮ້ອງຂໍການອະນຸຍາດ ດັ່ງນັ້ນຈຶ່ງບໍ່ສາມາດຢັ້ງຢືນຄຳຕອບຂອງທ່ານໄດ້."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ແຕະໃສ່ຄຸນສົມບັດໃດໜຶ່ງເພື່ອເລີ່ມການນຳໃຊ້ມັນ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບປຸ່ມການຊ່ວຍເຂົ້າເຖິງ"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບທາງລັດປຸ່ມລະດັບສຽງ"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"ປິດ <xliff:g id="SERVICE_NAME">%s</xliff:g> ໄວ້ແລ້ວ"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ແກ້ໄຂທາງລັດ"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ແລ້ວໆ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ad30d80..8eace89 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1748,7 +1748,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Programa užstoja leidimo užklausą, todėl negalima patvirtinti jūsų atsakymo."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Norėdami naudoti funkciją, palieskite ją:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Funkcijų, kurioms bus naudojamas pritaikomumo mygtukas, pasirinkimas"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Funkcijų, kurioms bus naudojamas garsumo spartusis klavišas, pasirinkimas"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Paslauga „<xliff:g id="SERVICE_NAME">%s</xliff:g>“ išjungta"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Redaguoti sparčiuosius klavišus"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Atlikta"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 329bbc3..01fb9da 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Kāda lietotne padara atļaujas pieprasījumu nesaprotamu, tāpēc nevar verificēt jūsu atbildi."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Pieskarieties funkcijai, lai sāktu to izmantot"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Izvēlieties funkcijas, ko izmantot ar pieejamības pogu"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Izvēlieties funkcijas, ko izmantot ar skaļuma pogu saīsni"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Pakalpojums <xliff:g id="SERVICE_NAME">%s</xliff:g> ir izslēgts."</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Rediģēt īsinājumtaustiņus"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gatavs"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 4bb0340..dbc4b75 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Апликација го прикрива барањето за дозвола, па вашиот одговор не може да се потврди."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Допрете на функција за да почнете да ја користите:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Изберете ги функциите што ќе ги користите со копчето за пристапност"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Изберете ги функциите што ќе ги користите со кратенката за копчето за јачина на звук"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> е исклучена"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Изменете ги кратенките"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 72e522d..2b1c52f 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ഒരു ആപ്പ്, അനുമതി അഭ്യർത്ഥന മറയ്ക്കുന്നതിനാൽ നിങ്ങളുടെ പ്രതികരണം പരിശോധിച്ചുറപ്പിക്കാനാകില്ല."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ഉപയോഗിച്ച് തുടങ്ങാൻ ഫീച്ചർ ടാപ്പ് ചെയ്യുക:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ഉപയോഗസഹായി ബട്ടണിന്റെ സഹായത്തോടെ, ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"വോളിയം കീ കുറുക്കുവഴിയിലൂടെ ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ഓഫാക്കിയിരിക്കുന്നു"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"കുറുക്കുവഴികൾ തിരുത്തുക"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"പൂർത്തിയാക്കി"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 4398975..bf2f78c 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Апп зөвшөөрлийн хүсэлтийг хааж байгаа тул таны хариултыг баталгаажуулах боломжгүй."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Үүнийг ашиглаж эхлэхийн тулд онцлог дээр товшино уу:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Хандалтын товчлуурын тусламжтай ашиглах онцлогуудыг сонгоно уу"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дууны түвшний түлхүүрийн товчлолын тусламжтай ашиглах онцлогуудыг сонгоно уу"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g>-г унтраалаа"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Товчлолуудыг засах"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Болсон"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index ac39d55..67c3b98 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"परवानगी मागणारी विनंती अॅपमुळे अस्पष्ट होत असल्याने, तुमच्या प्रतिसादाची पडताळणी केली जाऊ शकत नाही."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"वैशिष्ट्य वापरणे सुरू करण्यासाठी त्यावर टॅप करा:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"अॅक्सेसिबिलिटी बटणासोबत वापरायची असलेली ॲप्स निवडा"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"व्हॉल्यूम की शॉर्टकटसोबत वापरायची असलेली ॲप्स निवडा"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> बंद केले आहे"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"शॉर्टकट संपादित करा"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"पूर्ण झाले"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 88eef46..5eac793 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Apl menghalang permintaan kebenaran, maka jawapan anda tidak dapat disahkan."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ketik ciri untuk mula menggunakan ciri itu:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pilih ciri untuk digunakan dengan butang kebolehaksesan"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pilih ciri untuk digunakan dengan pintasan kekunci kelantangan"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> telah dimatikan"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit pintasan"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Selesai"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index d5c1fbd..b09539c 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"အက်ပ်တစ်ခုသည် ခွင့်ပြုချက်တောင်းဆိုမှုကို ပိတ်နေသဖြင့် သင့်တုံ့ပြန်မှုကို စိစစ်၍မရပါ။"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ဝန်ဆောင်မှုကို စတင်အသုံးပြုရန် တို့ပါ−"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"အများသုံးနိုင်မှု ခလုတ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"အသံခလုတ် ဖြတ်လမ်းလင့်ခ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ကို ပိတ်ထားသည်"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ဖြတ်လမ်းများကို တည်းဖြတ်ရန်"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ပြီးပြီ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 53b6ad5..8feb6e9 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"En app dekker forespørselen om tillatelse, så svaret ditt kan ikke bekreftes."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Trykk på en funksjon for å begynne å bruke den:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Velg funksjonene du vil bruke med Tilgjengelighet-knappen"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Velg funksjonene du vil bruke med volumtastsnarveien"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> er slått av"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Endre snarveier"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Ferdig"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 2a23e87..9f3530a 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1097,7 +1097,7 @@
<string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"यस पृष्ठलाई छोड्नुहोस्"</string>
<string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"यही पृष्ठमा रहनुहोस्"</string>
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nके तपाईं यो पेजबाट नेभिगेट गर्न चाहनु हुन्छ भन्ने निश्चत छ?"</string>
- <string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> मार्फत स्वतः भरण गर्नुहोस्"</string>
+ <string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> मार्फत अटोफिल गर्नुहोस्"</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"एउटा आलर्म सेट गर्नुहोस्"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"स्थापना गरिएको सङ्केत घडी एपमा सङ्केत समय मिलाउन एपलाई अनुमति दिन्छ। केही सङ्केत घडी एपहरूले यो सुविधा कार्यान्वयन नगर्न सक्छन्।"</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"भ्वाइसमेल थप गर्नुहोस्"</string>
@@ -1184,7 +1184,7 @@
<string name="selectTextMode" msgid="3225108910999318778">"पाठ चयन गर्नुहोस्"</string>
<string name="undo" msgid="3175318090002654673">"अन्डू गर्नुहोस्"</string>
<string name="redo" msgid="7231448494008532233">"रिडू गर्नुहोस्"</string>
- <string name="autofill" msgid="511224882647795296">"स्वतः भरण"</string>
+ <string name="autofill" msgid="511224882647795296">"अटोफिल"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"पाठ चयनता"</string>
<string name="addToDictionary" msgid="8041821113480950096">"शब्दकोशमा थप्नुहोस्"</string>
<string name="deleteText" msgid="4200807474529938112">"मेट्नुहोस्"</string>
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"कुनै एपका कारण अनुमतिसम्बन्धी अनुरोध बुझ्न कठिनाइ भइरहेकाले तपाईंको जवाफको पुष्टि गर्न सकिएन।"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"कुनै सुविधा प्रयोग गर्न थाल्न उक्त सुविधामा ट्याप गर्नुहोस्:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"पहुँचको बटनमार्फत प्रयोग गर्न चाहेका सुविधाहरू छनौट गर्नुहोस्"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"भोल्युम कुञ्जीको सर्टकटमार्फत प्रयोग गर्न चाहेका सुविधाहरू छनौट गर्नुहोस्"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> निष्क्रिय पारिएको छ"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"सर्टकटहरू सम्पादन गर्नुहोस्"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"सम्पन्न भयो"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5106f7c..8209504 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1721,7 +1721,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Verwijderen"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Wil je blijven luisteren op hoog volume?\n\nHet hoofdtelefoonvolume is langer dan de aanbevolen tijd hoog geweest. Dit kan je gehoor beschadigen."</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Wil je blijven luisteren op hoog volume?\n\nHet koptelefoonvolume is langer dan de aanbevolen tijd hoog geweest. Dit kan je gehoor beschadigen."</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Hard geluid gedetecteerd\n\nHet hoofdtelefoonvolume is hoger dan aanbevolen. Dit kan je gehoor beschadigen."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Snelkoppeling toegankelijkheid gebruiken?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Als de snelkoppeling aanstaat, houd je beide volumeknoppen 3 seconden ingedrukt om een toegankelijkheidsfunctie te starten."</string>
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Een app dekt het verzoek om rechten af, waardoor je reactie niet kan worden geverifieerd."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tik op een functie om deze te gebruiken:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Functies kiezen voor gebruik met de knop Toegankelijkheid"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Functies kiezen voor gebruik met de sneltoets via de volumeknop"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is uitgezet"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Snelkoppelingen bewerken"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index f9e9374..45b6e9a 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ଏକ ଆପ ଅନୁମତି ଅନୁରୋଧକୁ ଅସ୍ପଷ୍ଟ କରୁଛି ତେଣୁ ଆପଣଙ୍କ ଉତ୍ତରକୁ ଯାଞ୍ଚ କରାଯାଇପାରିବ ନାହିଁ।"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ଏକ ଫିଚର୍ ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ଏହାକୁ ଟାପ୍ କରନ୍ତୁ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ଆକ୍ସେସିବିଲିଟୀ ବଟନ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ଭଲ୍ୟୁମ୍ କୀ ସର୍ଟକଟ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ବନ୍ଦ ହୋଇଯାଇଛି"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ସର୍ଟକଟଗୁଡ଼ିକୁ ଏଡିଟ କରନ୍ତୁ"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ହୋଇଗଲା"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index e309832..c6d0b9f 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ਕੋਈ ਐਪ ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਬੇਨਤੀ ਨੂੰ ਅਸਪਸ਼ਟ ਕਰ ਰਹੀ ਹੈ, ਇਸ ਲਈ ਤੁਹਾਡੇ ਜਵਾਬ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ਕਿਸੇ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਵਰਤਣਾ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਉਸ \'ਤੇ ਟੈਪ ਕਰੋ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨਾਲ ਵਰਤਣ ਲਈ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚੁਣੋ"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ਅਵਾਜ਼ ਕੁੰਜੀ ਸ਼ਾਰਟਕੱਟ ਨਾਲ ਵਰਤਣ ਲਈ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚੁਣੋ"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ਸ਼ਾਰਟਕੱਟਾਂ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ਹੋ ਗਿਆ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b6f70d6..0ae3bbf 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1748,7 +1748,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacja zasłania prośbę o uprawnienia, więc nie można zweryfikować Twojej odpowiedzi."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Wybierz funkcję, aby zacząć z niej korzystać:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Wybierz funkcje, których chcesz używać z przyciskiem ułatwień dostępu"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Wybierz funkcje, do których chcesz używać skrótu z klawiszami głośności"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usługa <xliff:g id="SERVICE_NAME">%s</xliff:g> została wyłączona"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edytuj skróty"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"OK"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d5035e0..e025d19 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Um app está ocultando a solicitação de permissão e impedindo a verificação da sua resposta."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque em um recurso para começar a usá-lo:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha recursos para usar com o botão de acessibilidade"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha recursos para usar com o atalho da tecla de volume"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"O <xliff:g id="SERVICE_NAME">%s</xliff:g> foi desativado"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar atalhos"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Concluído"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index a9ba018..71940ef 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Uma app está a ocultar o pedido de autorização e, por isso, não é possível validar a sua resposta."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque numa funcionalidade para começar a utilizá-la:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha funcionalidades para utilizar com o botão Acessibilidade"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha funcionalidades para usar com o atalho das teclas de volume"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"O serviço <xliff:g id="SERVICE_NAME">%s</xliff:g> foi desativado."</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar atalhos"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Concluído"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d5035e0..e025d19 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Um app está ocultando a solicitação de permissão e impedindo a verificação da sua resposta."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque em um recurso para começar a usá-lo:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha recursos para usar com o botão de acessibilidade"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha recursos para usar com o atalho da tecla de volume"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"O <xliff:g id="SERVICE_NAME">%s</xliff:g> foi desativado"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar atalhos"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Concluído"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 444dbd4..7e5e7c0 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"O aplicație blochează solicitarea de permisiune, așa că răspunsul nu se poate verifica."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Atinge o funcție ca să începi să o folosești:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Alege funcțiile pe care să le folosești cu butonul de accesibilitate"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Alege funcțiile pentru comanda rapidă a butonului de volum"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> a fost dezactivat"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editează comenzile rapide"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gata"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 46e7b9d..22501f2 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1748,7 +1748,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Невозможно принять ваш ответ, поскольку запрос разрешения скрыт другим приложением."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Выберите, какую функцию использовать:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Выберите функции, которые будут запускаться с помощью кнопки специальных возможностей"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберите функции, которые будут запускаться с помощью кнопки регулировки громкости"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сервис \"<xliff:g id="SERVICE_NAME">%s</xliff:g>\" отключен."</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Изменить ярлыки"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index fb03569..af835d9 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"යෙදුමක් අවසර ඉල්ලීම අඳුරු කරන බැවින්, ඔබේ ප්රතිචාරය සත්යාපනය කළ නොහැක."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"එය භාවිත කිරීම ආරම්භ කිරීමට විශේෂාංගයක් තට්ටු කරන්න:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ප්රවේශ්යතා බොත්තම සමග භාවිත කිරීමට විශේෂාංග තෝරා ගන්න"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"හඬ පරිමා යතුරු කෙටිමග සමග භාවිත කිරීමට විශේෂාංග තෝරා ගන්න"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ක්රියාවිරහිත කර ඇත"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"කෙටිමං සංස්කරණ කරන්න"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"නිමයි"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index bde470e..3289bbc 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1748,7 +1748,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikácia zakrýva žiadosť o povolenie, takže vaša odpoveď sa nedá overiť."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Klepnutím na funkciu ju začnite používať:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Výber funkcií, ktoré chcete používať tlačidlom dostupnosti"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Výber funkcií, ktoré chcete používať klávesovou skratkou"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Služba <xliff:g id="SERVICE_NAME">%s</xliff:g> bola vypnutá"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Upraviť skratky"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hotovo"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index b70322b..99ce046 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1748,7 +1748,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacija zakriva zahtevo za dovoljenje, zato ni mogoče potrditi vašega odgovora."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Če želite začeti uporabljati funkcijo, se je dotaknite:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Izberite funkcije, ki jih želite uporabljati z gumbom za dostopnost"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Izberite funkcije, ki jih želite uporabljati z bližnjico na tipki za glasnost"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Storitev <xliff:g id="SERVICE_NAME">%s</xliff:g> je izklopljena"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Uredi bližnjice"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Končano"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 3040597..d8fe109 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Një aplikacion po fsheh kërkesën për leje, prandaj përgjigja jote nuk mund të verifikohet."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Trokit te një veçori për të filluar ta përdorësh atë:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Zgjidh veçoritë që do të përdorësh me butonin e qasshmërisë"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Zgjidh veçoritë që do të përdorësh me shkurtoren e tastit të volumit"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> është çaktivizuar"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Redakto shkurtoret"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"U krye"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f8e5a79..0451b56 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1747,7 +1747,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Апликација крије захтев за дозволу, па одговор не може да се верификује."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Додирните неку функцију да бисте почели да је користите:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Одаберите функције које ћете користити са дугметом Приступачност"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Одаберите функције за пречицу тастером јачине звука"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Услуга <xliff:g id="SERVICE_NAME">%s</xliff:g> је искључена"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Измените пречице"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 8e08c6b..9bde6af 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"En app döljer behörighetsbegäran så det går inte att verifiera svaret."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tryck på funktioner som du vill aktivera:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Välj vilka funktioner du vill använda med hjälp av tillgänglighetsknappen"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Välj att funktioner att använda med hjälp av volymknappskortkommandot"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> har inaktiverats"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Redigera genvägar"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klar"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index a9385c1..c286eeb 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Programu inazuia ombi la ruhusa kwa hivyo jibu lako haliwezi kuthibitishwa."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Gusa kipengele ili uanze kukitumia:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Chagua vipengele vya kutumia na kitufe cha zana za ufikivu"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Chagua vipengele vya kutumia na njia ya mkato ya kitufe cha sauti"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Huduma ya <xliff:g id="SERVICE_NAME">%s</xliff:g> imezimwa"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Kubadilisha njia za mkato"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Nimemaliza"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 6ba7a54..b1ce514 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"அணுகல் கோரிக்கையை ஓர் ஆப்ஸ் மறைப்பதால் உங்கள் பதிலைச் சரிபார்க்க முடியாது."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ஒரு அம்சத்தைப் பயன்படுத்த அதைத் தட்டவும்:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"அணுகல்தன்மை பட்டன் மூலம் பயன்படுத்த விரும்பும் அம்சங்களைத் தேர்வுசெய்யுங்கள்"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ஒலியளவு விசை ஷார்ட்கட் மூலம் பயன்படுத்த விரும்பும் அம்சங்களைத் தேர்வுசெய்யுங்கள்"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ஆஃப் செய்யப்பட்டுள்ளது"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ஷார்ட்கட்களை மாற்று"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"முடிந்தது"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index ae1a2c3..4b655d1 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ఒక యాప్ అనుమతి రిక్వెస్ట్కు అడ్డు తగులుతోంది కాబట్టి మీ సమాధానం వెరిఫై చేయడం సాధ్యం కాదు."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ఫీచర్ని ఉపయోగించడం ప్రారంభించడానికి, దాన్ని ట్యాప్ చేయండి:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"యాక్సెసిబిలిటీ బటన్తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"వాల్యూమ్ కీ షార్ట్కట్తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ఆఫ్ చేయబడింది"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"షార్ట్కట్లను ఎడిట్ చేయండి"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"పూర్తయింది"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 655cf4c..d979c34 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"มีแอปหนึ่งบดบังคำขอสิทธิ์ เราจึงยืนยันการตอบกลับของคุณไม่ได้"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"แตะฟีเจอร์เพื่อเริ่มใช้"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"เลือกฟีเจอร์ที่จะใช้กับปุ่มการช่วยเหลือพิเศษ"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"เลือกฟีเจอร์ที่จะใช้กับทางลัดปุ่มปรับระดับเสียง"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"ปิด <xliff:g id="SERVICE_NAME">%s</xliff:g> แล้ว"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"แก้ไขทางลัด"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"เสร็จ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 885be75..fbbb32c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1721,7 +1721,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Alisin"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Magpatuloy sa pakikinig nang may malakas na volume?\n\nNaging malakas ang volume nang mas matagal na sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Magpatuloy sa pakikinig nang may malakas na volume?\n\nMalakas ang volume nang mas matagal na sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Naka-detect ng malakas na tunog\n\nMas malakas ang volume kaysa sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gagamitin ang Shortcut sa Accessibility?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume."</string>
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"May app na pumipigil sa kahilingan sa pahintulot kaya hindi ma-verify ang iyong sagot."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"I-tap ang isang feature para simulan itong gamitin:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pumili ng mga feature na gagana sa pamamagitan ng button ng accessibility"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pumili ng mga feature na gagana sa pamamagitan ng shortcut ng volume key"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Na-off ang <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"I-edit ang mga shortcut"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Tapos na"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a5d064e..a015a8f 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Bir uygulama, izin isteğini gizlediğinden yanıtınız doğrulanamıyor."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Kullanmaya başlamak için bir özelliğe dokunun:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Erişilebilirlik düğmesiyle kullanılacak özellikleri seçin"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Ses tuşu kısayoluyla kullanılacak özellikleri seçin"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kapatıldı"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Kısayolları düzenle"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Bitti"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 5514ba4..e41d3ce 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1748,7 +1748,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Інший додаток перекриває запит на доступ, тому вашу відповідь не вдається підтвердити."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Натисніть функцію, щоб почати використовувати її:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Виберіть функції для кнопки спеціальних можливостей"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Виберіть функції для комбінації з клавішами гучності"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сервіс <xliff:g id="SERVICE_NAME">%s</xliff:g> вимкнено"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Змінити"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 7c6317c..d2f0898 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ایپ اجازت کی درخواست کو مبہم کر رہی ہے لہذا آپ کے جواب کی تصدیق نہیں کی جا سکتی۔"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ایک خصوصیت کا استعمال شروع کرنے کیلئے اسے تھپتھپائیں:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ایکسیسبیلٹی بٹن کے ساتھ استعمال کرنے کیلئے خصوصیات منتخب کریں"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"والیوم کلید کے شارٹ کٹ کے ساتھ استعمال کرنے کیلئے خصوصیات منتخب کریں"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> کو آف کر دیا گیا ہے"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"شارٹ کٹس میں ترمیم کریں"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ہو گیا"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 868a927..e9bc805 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Ilova ruxsat olish talabini berkitmoqda, shu sababdan javobingizni tasdiqlash imkonsiz."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Kerakli funksiyani tanlang"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Qulayliklar tugmasi bilan foydalanish uchun funksiyalarni tanlang"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Tovush tugmasi bilan ishga tushiriladigan funksiyalarni tanlang"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> faolsizlantirildi"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Tezkor tugmalarni tahrirlash"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"OK"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index c7337ef..510edab 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Một ứng dụng đang che khuất yêu cầu quyền này nên chúng tôi không thể xác minh phản hồi của bạn."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Nhấn vào một tính năng để bắt đầu sử dụng:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Chọn các tính năng để dùng với nút hỗ trợ tiếp cận"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Chọn các tính năng để dùng với phím tắt là phím âm lượng"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> đã bị tắt"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Chỉnh sửa phím tắt"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Xong"</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 5266214..e28b646 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -96,4 +96,8 @@
<!-- True if the device supports system decorations on secondary displays. -->
<bool name="config_supportsSystemDecorsOnSecondaryDisplays">false</bool>
+
+ <!-- Whether to enable scaling and fading animation to scrollviews while scrolling.
+ P.S this is a change only intended for wear devices. -->
+ <bool name="config_enableViewGroupScalingFading">true</bool>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 378e548..bcb54dc 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"应用遮挡了权限请求,因此我们无法验证您的回复。"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"点按相应功能即可开始使用:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"选择可通过“无障碍”按钮使用的功能"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"选择可通过音量键快捷方式使用的功能"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"已关闭<xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"修改快捷方式"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"完成"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 5a9db4f..875eec0 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"有應用程式阻擋權限要求,因此系統無法驗證你的回應。"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"輕按即可開始使用所需功能:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"選擇要配搭無障礙功能按鈕使用的功能"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"選擇要用音量快速鍵的功能"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> 已關閉"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"編輯捷徑"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"完成"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5cfca16..efa4c51 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"應用程式遮擋了權限要求,因此系統無法驗證你的回覆。"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"輕觸即可開始使用所需功能:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"選擇要搭配無障礙工具按鈕使用的功能"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"選擇要搭配音量快速鍵使用的功能"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"「<xliff:g id="SERVICE_NAME">%s</xliff:g>」已關閉"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"編輯捷徑"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"完成"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index c8e49a1..67a4361 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1746,7 +1746,8 @@
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"I-app ifihla isicelo semvume ngakho impendulo yakho ayikwazi ukuqinisekiswa."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Thepha isici ukuqala ukusisebenzisa:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Khetha izici ongazisebenzisa nenkinobho yokufinyeleleka"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Khetha izici ongazisebenzisa nesinqamuleli sokhiye wevolumu"</string>
+ <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+ <skip />
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"I-<xliff:g id="SERVICE_NAME">%s</xliff:g> ivaliwe"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Hlela izinqamuleli"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Kwenziwe"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b6468ee..f6267f6 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>
@@ -7030,6 +7033,10 @@
{@link InputDevice#SOURCE_ROTARY_ENCODER}s. -->
<bool name="config_viewBasedRotaryEncoderHapticsEnabled">false</bool>
+ <!-- Whether the scroll haptic feedback implementation is enabled for
+ {@link InputDevice#SOURCE_TOUCHSCREEN}s. -->
+ <bool name="config_viewTouchScreenHapticScrollFeedbackEnabled">false</bool>
+
<!-- Whether the media player is shown on the quick settings -->
<bool name="config_quickSettingsShowMediaPlayer">true</bool>
@@ -7114,4 +7121,8 @@
<!-- The maximum number of call log entries for each sim card that can be stored in the call log
provider on the device. -->
<integer name="config_maximumCallLogEntriesPerSim">500</integer>
+
+ <!-- Whether to enable scaling and fading animation to scrollviews while scrolling.
+ P.S this is a change only intended for wear devices. -->
+ <bool name="config_enableViewGroupScalingFading">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f404666..d634210 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4804,9 +4804,9 @@
</string>
<!-- Title for accessibility edit shortcut selection menu dialog, and dialog is triggered
- from volume key shortcut. [CHAR LIMIT=100] -->
+ from volume keys shortcut. [CHAR LIMIT=100] -->
<string name="accessibility_edit_shortcut_menu_volume_title">Choose features to use with the
- volume key shortcut
+ volume keys shortcut
</string>
<!-- Text for showing the warning to user when uncheck the legacy app item in the accessibility
@@ -5509,6 +5509,9 @@
<!-- Message shown in the dialog prompting the user to set up a screen lock to access private space [CHAR LIMIT=120] -->
<string name="private_space_set_up_screen_lock_message">To use your private space, set a screen lock on this device</string>
+ <!-- Message shown in the dialog prompting the user to set up a screen lock to delete private space from Reset Options [CHAR LIMIT=120] -->
+ <string name="private_space_set_up_screen_lock_for_reset">To delete private space, set a screen lock on this device</string>
+
<!-- Title of the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=50] -->
<string name="app_blocked_title">App is not available</string>
<!-- Default message shown in the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 74922ac..bd8077e 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" />
@@ -3298,6 +3299,8 @@
<java-symbol type="string" name="set_up_screen_lock_action_label" />
<!-- Message for the alert dialog prompting the user to set up a screen lock to access private space -->
<java-symbol type="string" name="private_space_set_up_screen_lock_message" />
+ <!-- Message shown in the dialog prompting the user to set up a screen lock to delete private space from Reset Options [CHAR LIMIT=120] -->
+ <java-symbol type="string" name="private_space_set_up_screen_lock_for_reset" />
<java-symbol type="string" name="deprecated_target_sdk_message" />
<java-symbol type="string" name="deprecated_target_sdk_app_store" />
@@ -5490,8 +5493,11 @@
<java-symbol type="bool" name="config_tvExternalInputLoggingDisplayNameFilterEnabled" />
<java-symbol type="array" name="config_tvExternalInputLoggingDeviceOnScreenDisplayNames" />
<java-symbol type="array" name="config_tvExternalInputLoggingDeviceBrandNames" />
+
+ <!-- Scroll Feedback Configs -->
<java-symbol type="bool" name="config_viewRotaryEncoderHapticScrollFedbackEnabled" />
<java-symbol type="bool" name="config_viewBasedRotaryEncoderHapticsEnabled" />
+ <java-symbol type="bool" name="config_viewTouchScreenHapticScrollFeedbackEnabled" />
<java-symbol type="bool" name="config_quickSettingsShowMediaPlayer" />
@@ -5597,4 +5603,6 @@
<!-- Fingerprint loe notification string -->
<java-symbol type="string" name="fingerprint_loe_notification_msg" />
+
+ <java-symbol type="bool" name="config_enableViewGroupScalingFading"/>
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 452ae04..d35bfb7 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -131,7 +131,7 @@
<item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.ProgressBar.Large.Inverse</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="seekBarStyle">@style/Widget.DeviceDefault.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.DeviceDefault.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.RatingBar.Indicator</item>
@@ -351,7 +351,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -468,7 +468,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -587,7 +587,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -705,7 +705,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -831,7 +831,7 @@
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -948,7 +948,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -1064,7 +1064,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -1181,7 +1181,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -1314,7 +1314,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -1432,7 +1432,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -1548,7 +1548,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -1666,7 +1666,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -1783,7 +1783,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -1900,7 +1900,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -2017,7 +2017,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -2134,7 +2134,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -2251,7 +2251,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -2373,7 +2373,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -2488,7 +2488,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -2642,7 +2642,7 @@
<item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Large.Inverse</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="seekBarStyle">@style/Widget.DeviceDefault.Light.SeekBar</item>
<item name="ratingBarStyle">@style/Widget.DeviceDefault.Light.RatingBar</item>
<item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.Light.RatingBar.Indicator</item>
@@ -2858,7 +2858,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -2974,7 +2974,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -3091,7 +3091,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -3210,7 +3210,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -3328,7 +3328,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -3452,7 +3452,7 @@
<item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -3572,7 +3572,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -3691,7 +3691,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -3811,7 +3811,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -4133,7 +4133,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -4254,7 +4254,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -4373,7 +4373,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -4491,7 +4491,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -4608,7 +4608,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -4725,7 +4725,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -4840,7 +4840,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -5066,7 +5066,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -5162,7 +5162,7 @@
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -5280,7 +5280,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -5471,7 +5471,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -5522,7 +5522,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
@@ -5641,7 +5641,7 @@
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+ <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
<item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
<!-- Toolbar attributes -->
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
index 516253b..44beb55 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
@@ -19,18 +19,18 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
-import android.app.compat.CompatChanges;
import android.os.Binder;
import android.os.UserHandle;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
@@ -41,36 +41,40 @@
private static final int USER_ID_1 = 11;
private static final int USER_ID_2 = 12;
+ private RadioServiceUserController mUserController;
@Mock
private UserHandle mUserHandleMock;
+ @Rule
+ public final Expect expect = Expect.create();
+
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
- builder.spyStatic(ActivityManager.class).spyStatic(Binder.class)
- .spyStatic(CompatChanges.class);
+ builder.spyStatic(ActivityManager.class).spyStatic(Binder.class);
}
@Before
public void setUp() {
doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
doReturn(USER_ID_1).when(() -> ActivityManager.getCurrentUser());
+ mUserController = new RadioServiceUserControllerImpl();
}
@Test
public void isCurrentOrSystemUser_forCurrentUser_returnsFalse() {
when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
- assertWithMessage("Current user")
- .that(RadioServiceUserController.isCurrentOrSystemUser()).isTrue();
+ expect.withMessage("Current user")
+ .that(mUserController.isCurrentOrSystemUser()).isTrue();
}
@Test
public void isCurrentOrSystemUser_forNonCurrentUser_returnsFalse() {
when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_2);
- assertWithMessage("Non-current user")
- .that(RadioServiceUserController.isCurrentOrSystemUser()).isFalse();
+ expect.withMessage("Non-current user")
+ .that(mUserController.isCurrentOrSystemUser()).isFalse();
}
@Test
@@ -78,8 +82,8 @@
when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
- assertWithMessage("System user")
- .that(RadioServiceUserController.isCurrentOrSystemUser()).isTrue();
+ expect.withMessage("System user")
+ .that(mUserController.isCurrentOrSystemUser()).isTrue();
}
@Test
@@ -87,14 +91,14 @@
when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
doThrow(new RuntimeException()).when(ActivityManager::getCurrentUser);
- assertWithMessage("User when activity manager fails")
- .that(RadioServiceUserController.isCurrentOrSystemUser()).isFalse();
+ expect.withMessage("User when activity manager fails")
+ .that(mUserController.isCurrentOrSystemUser()).isFalse();
}
@Test
public void getCurrentUser() {
- assertWithMessage("Current user")
- .that(RadioServiceUserController.getCurrentUser()).isEqualTo(USER_ID_1);
+ expect.withMessage("Current user")
+ .that(mUserController.getCurrentUser()).isEqualTo(USER_ID_1);
}
@Test
@@ -102,7 +106,15 @@
when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
doThrow(new RuntimeException()).when(ActivityManager::getCurrentUser);
- assertWithMessage("Current user when activity manager fails")
- .that(RadioServiceUserController.getCurrentUser()).isEqualTo(UserHandle.USER_NULL);
+ expect.withMessage("Current user when activity manager fails")
+ .that(mUserController.getCurrentUser()).isEqualTo(UserHandle.USER_NULL);
+ }
+
+ @Test
+ public void getCallingUserId() {
+ when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
+
+ expect.withMessage("Calling user id")
+ .that(mUserController.getCallingUserId()).isEqualTo(USER_ID_1);
}
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
index 22f3bd4..63f12d8 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
@@ -91,12 +91,13 @@
private IAnnouncementListener mAnnouncementListenerMock;
@Mock
private IBinder mListenerBinderMock;
+ @Mock
+ private RadioServiceUserController mUserControllerMock;
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
builder.spyStatic(ServiceManager.class)
- .spyStatic(RadioModule.class)
- .spyStatic(RadioServiceUserController.class);
+ .spyStatic(RadioModule.class);
}
@Test
@@ -156,7 +157,7 @@
@Test
public void openSession_forNonCurrentUser_throwsException() throws Exception {
createBroadcastRadioService();
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
IllegalStateException thrown = assertThrows(IllegalStateException.class,
() -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
@@ -206,9 +207,9 @@
}
private void createBroadcastRadioService() throws RemoteException {
- doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(true).when(mUserControllerMock).isCurrentOrSystemUser();
mockServiceManager();
- mBroadcastRadioService = new BroadcastRadioServiceImpl(SERVICE_LIST);
+ mBroadcastRadioService = new BroadcastRadioServiceImpl(SERVICE_LIST, mUserControllerMock);
}
private void mockServiceManager() throws RemoteException {
@@ -222,9 +223,9 @@
any(IServiceCallback.class)));
doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
- eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class)));
+ eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class), any()));
doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
- eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class)));
+ eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class), any()));
when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
index a952bde..368df09 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
@@ -34,6 +34,8 @@
import android.os.ParcelableException;
import android.os.RemoteException;
+import com.android.server.broadcastradio.RadioServiceUserController;
+
import com.google.common.truth.Expect;
import org.junit.Before;
@@ -63,6 +65,8 @@
private IAnnouncementListener mListenerMock;
@Mock
private android.hardware.broadcastradio.ICloseHandle mHalCloseHandleMock;
+ @Mock
+ private RadioServiceUserController mUserControllerMock;
// RadioModule under test
private RadioModule mRadioModule;
@@ -70,7 +74,8 @@
@Before
public void setup() throws RemoteException {
- mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES);
+ mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES,
+ mUserControllerMock);
// TODO(b/241118988): test non-null image for getImage method
when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 92dfe38..24d18e0 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -49,12 +49,10 @@
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
import android.hardware.radio.UniqueProgramIdentifier;
-import android.os.Binder;
import android.os.DeadObjectException;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
-import android.os.UserHandle;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -148,10 +146,10 @@
// Mocks
@Mock
- private UserHandle mUserHandleMock;
- @Mock
private IBroadcastRadio mBroadcastRadioMock;
private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
+ @Mock
+ private RadioServiceUserController mUserControllerMock;
// RadioModule under test
private RadioModule mRadioModule;
@@ -170,8 +168,7 @@
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
- builder.spyStatic(RadioServiceUserController.class).spyStatic(CompatChanges.class)
- .spyStatic(Binder.class);
+ builder.spyStatic(CompatChanges.class);
}
@Before
@@ -182,13 +179,12 @@
doReturn(true).when(() -> CompatChanges.isChangeEnabled(
eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
- doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier();
- doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
- doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
- doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
+ doReturn(USER_ID_1).when(mUserControllerMock).getCallingUserId();
+ doReturn(true).when(mUserControllerMock).isCurrentOrSystemUser();
+ doReturn(USER_ID_1).when(mUserControllerMock).getCurrentUser();
mRadioModule = new RadioModule(mBroadcastRadioMock,
- AidlTestUtils.makeDefaultModuleProperties());
+ AidlTestUtils.makeDefaultModuleProperties(), mUserControllerMock);
doAnswer(invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
@@ -237,7 +233,7 @@
@Test
public void setConfiguration_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
openAidlClients(/* numClients= */ 1);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
@@ -434,7 +430,7 @@
@Test
public void tune_forNonCurrentUser_doesNotTune() throws Exception {
openAidlClients(/* numClients= */ 1);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
RadioManager.ProgramInfo tuneInfo =
AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
@@ -514,7 +510,7 @@
openAidlClients(/* numClients= */ 1);
mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo(
ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
@@ -593,7 +589,7 @@
openAidlClients(/* numClients= */ 1);
mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo(
ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
@@ -627,7 +623,7 @@
@Test
public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].cancel();
@@ -698,7 +694,7 @@
@Test
public void startBackgroundScan_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
openAidlClients(/* numClients= */ 1);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].startBackgroundScan();
@@ -968,7 +964,7 @@
openAidlClients(/* numClients= */ 1);
ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
/* includeCategories= */ true, /* excludeModifications= */ false);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].startProgramListUpdates(filter);
@@ -1007,7 +1003,7 @@
ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
/* includeCategories= */ true, /* excludeModifications= */ false);
mTunerSessions[0].startProgramListUpdates(filter);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].stopProgramListUpdates();
@@ -1073,7 +1069,7 @@
public void setConfigFlag_forNonCurrentUser_doesNotSetConfigFlag() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG + 1;
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
@@ -1138,7 +1134,7 @@
openAidlClients(/* numClients= */ 1);
Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
"mockParam2", "mockValue2");
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].setParameters(parametersSet);
@@ -1192,7 +1188,7 @@
public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
throws Exception {
openAidlClients(1);
- doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
+ doReturn(USER_ID_2).when(mUserControllerMock).getCurrentUser();
mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo(
AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY));
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
index acf698b..7c3f221 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
@@ -88,11 +88,12 @@
private IAnnouncementListener mAnnouncementListenerMock;
@Mock
private IBinder mBinderMock;
+ @Mock
+ private RadioServiceUserController mUserControllerMock;
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
- builder.spyStatic(RadioModule.class)
- .spyStatic(RadioServiceUserController.class);
+ builder.spyStatic(RadioModule.class);
}
@Test
@@ -156,7 +157,7 @@
@Test
public void openSession_forNonCurrentUser_throwsException() throws Exception {
createBroadcastRadioService();
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ when(mUserControllerMock.isCurrentOrSystemUser()).thenReturn(false);
IllegalStateException thrown = assertThrows(IllegalStateException.class,
() -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
@@ -206,11 +207,11 @@
}
private void createBroadcastRadioService() throws RemoteException {
- doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ when(mUserControllerMock.isCurrentOrSystemUser()).thenReturn(true);
mockServiceManager();
mBroadcastRadioService = new BroadcastRadioService(/* nextModuleId= */ FM_RADIO_MODULE_ID,
- mServiceManagerMock);
+ mServiceManagerMock, mUserControllerMock);
}
private void mockServiceManager() throws RemoteException {
@@ -231,9 +232,9 @@
}).thenReturn(true);
doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
- eq(FM_RADIO_MODULE_ID), anyString()));
+ eq(FM_RADIO_MODULE_ID), anyString(), any()));
doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
- eq(DAB_RADIO_MODULE_ID), anyString()));
+ eq(DAB_RADIO_MODULE_ID), anyString(), any()));
when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
index 1f5e770..b53f7ca 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
@@ -36,6 +36,8 @@
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
+import com.android.server.broadcastradio.RadioServiceUserController;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,13 +63,16 @@
private IAnnouncementListener mListenerMock;
@Mock
private android.hardware.broadcastradio.V2_0.ICloseHandle mHalCloseHandleMock;
+ @Mock
+ private RadioServiceUserController mUserControllerMock;
private RadioModule mRadioModule;
private android.hardware.broadcastradio.V2_0.IAnnouncementListener mHalListener;
@Before
public void setup() throws RemoteException {
- mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES);
+ mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES,
+ mUserControllerMock);
when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 8c16d79..fa07447 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -38,13 +38,13 @@
import android.hardware.radio.UniqueProgramIdentifier;
import android.os.RemoteException;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
import com.android.server.broadcastradio.RadioServiceUserController;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.mockito.verification.VerificationWithTimeout;
@@ -55,7 +55,8 @@
/**
* Tests for v2 HAL RadioModule.
*/
-public class StartProgramListUpdatesFanoutTest extends ExtendedRadioMockitoTestCase {
+@RunWith(MockitoJUnitRunner.class)
+public class StartProgramListUpdatesFanoutTest {
private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
private static final VerificationWithTimeout CB_TIMEOUT = timeout(500);
@@ -64,6 +65,8 @@
@Mock IBroadcastRadio mBroadcastRadioMock;
@Mock ITunerSession mHalTunerSessionMock;
private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
+ @Mock
+ private RadioServiceUserController mUserControllerMock;
// RadioModule under test
private RadioModule mRadioModule;
@@ -110,17 +113,12 @@
private static final RadioManager.ProgramInfo TEST_DAB_INFO = TestUtils.makeProgramInfo(
TEST_DAB_SELECTOR, TEST_QUALITY);
- @Override
- protected void initializeSession(StaticMockitoSessionBuilder builder) {
- builder.spyStatic(RadioServiceUserController.class);
- }
-
@Before
public void setup() throws RemoteException {
- doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(true).when(mUserControllerMock).isCurrentOrSystemUser();
- mRadioModule = new RadioModule(mBroadcastRadioMock,
- TestUtils.makeDefaultModuleProperties());
+ mRadioModule = new RadioModule(mBroadcastRadioMock, TestUtils.makeDefaultModuleProperties(),
+ mUserControllerMock);
doAnswer((Answer) invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 55aae9d..62445cf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -44,16 +44,12 @@
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
-import android.os.Binder;
import android.os.DeadObjectException;
import android.os.ParcelableException;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
import com.android.server.broadcastradio.RadioServiceUserController;
import com.google.common.truth.Expect;
@@ -76,7 +72,7 @@
* Tests for HIDL HAL TunerSession.
*/
@RunWith(MockitoJUnitRunner.class)
-public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase {
+public final class TunerSessionHidlTest {
private static final int USER_ID_1 = 11;
private static final int USER_ID_2 = 12;
@@ -104,27 +100,21 @@
public final Expect mExpect = Expect.create();
@Mock
- private UserHandle mUserHandleMock;
- @Mock
private IBroadcastRadio mBroadcastRadioMock;
@Mock
ITunerSession mHalTunerSessionMock;
private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
-
- @Override
- protected void initializeSession(StaticMockitoSessionBuilder builder) {
- builder.spyStatic(RadioServiceUserController.class).spyStatic(Binder.class);
- }
+ @Mock
+ private RadioServiceUserController mUserControllerMock;
@Before
public void setup() throws Exception {
- doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier();
- doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
- doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
- doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
+ doReturn(USER_ID_1).when(mUserControllerMock).getCallingUserId();
+ doReturn(true).when(mUserControllerMock).isCurrentOrSystemUser();
+ doReturn(USER_ID_1).when(mUserControllerMock).getCurrentUser();
mRadioModule = new RadioModule(mBroadcastRadioMock,
- TestUtils.makeDefaultModuleProperties());
+ TestUtils.makeDefaultModuleProperties(), mUserControllerMock);
doAnswer(invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
@@ -228,7 +218,7 @@
@Test
public void setConfiguration_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
openAidlClients(/* numClients= */ 1);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
@@ -403,7 +393,7 @@
@Test
public void tune_forNonCurrentUser_doesNotTune() throws Exception {
openAidlClients(/* numClients= */ 1);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
RadioManager.ProgramInfo tuneInfo =
TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
@@ -481,7 +471,7 @@
openAidlClients(/* numClients= */ 1);
mHalCurrentInfo = TestUtils.makeHalProgramInfo(
Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
@@ -559,7 +549,7 @@
openAidlClients(/* numClients= */ 1);
mHalCurrentInfo = TestUtils.makeHalProgramInfo(
Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
@@ -593,7 +583,7 @@
@Test
public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].cancel();
@@ -663,7 +653,7 @@
@Test
public void startBackgroundScan_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
openAidlClients(/* numClients= */ 1);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].startBackgroundScan();
@@ -676,7 +666,7 @@
openAidlClients(/* numClients= */ 1);
ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
/* includeCategories= */ true, /* excludeModifications= */ false);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].startProgramListUpdates(filter);
@@ -715,7 +705,7 @@
ProgramList.Filter aidlFilter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
/* includeCategories= */ true, /* excludeModifications= */ false);
mTunerSessions[0].startProgramListUpdates(aidlFilter);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].stopProgramListUpdates();
@@ -781,7 +771,7 @@
public void setConfigFlag_forNonCurrentUser_doesNotSetConfigFlag() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG + 1;
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
@@ -846,7 +836,7 @@
openAidlClients(/* numClients= */ 1);
Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
"mockParam2", "mockValue2");
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
mTunerSessions[0].setParameters(parametersSet);
@@ -900,7 +890,7 @@
public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
throws Exception {
openAidlClients(1);
- doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
+ doReturn(USER_ID_2).when(mUserControllerMock).getCurrentUser();
mHalTunerCallback.onCurrentProgramInfoChanged(TestUtils.makeHalProgramInfo(
TestUtils.makeHalFmSelector(/* freq= */ 97300), SIGNAL_QUALITY));
diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp
index 9d84685..1abceb8 100644
--- a/core/tests/GameManagerTests/Android.bp
+++ b/core/tests/GameManagerTests/Android.bp
@@ -37,3 +37,10 @@
certificate: "platform",
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "FrameworksCoreGameManagerTests_android_app",
+ base: "FrameworksCoreGameManagerTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.app"],
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 7954c5a7..5111d2d 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -21,6 +21,7 @@
srcs: [
"DisabledTestApp/src/**/*.java",
"EnabledTestApp/src/**/*.java",
+ "BinderFrozenStateChangeCallbackTestApp/src/**/*.java",
"BinderProxyCountingTestApp/src/**/*.java",
"BinderProxyCountingTestService/src/**/*.java",
"BinderDeathRecipientHelperApp/src/**/*.java",
@@ -138,6 +139,7 @@
":BinderDeathRecipientHelperApp1",
":BinderDeathRecipientHelperApp2",
":com.android.cts.helpers.aosp",
+ ":BinderFrozenStateChangeCallbackTestApp",
":BinderProxyCountingTestApp",
":BinderProxyCountingTestService",
":AppThatUsesAppOps",
@@ -297,11 +299,6 @@
auto_gen_config: true,
}
-FLAKY_OR_IGNORED = [
- "androidx.test.filters.FlakyTest",
- "org.junit.Ignore",
-]
-
test_module_config {
name: "FrameworksCoreTests_Presubmit",
base: "FrameworksCoreTests",
@@ -310,7 +307,6 @@
"device-platinum-tests",
],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -335,7 +331,6 @@
"device-platinum-tests",
],
include_filters: ["android.content.ContextTest"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -346,7 +341,6 @@
"device-platinum-tests",
],
include_filters: ["android.app.KeyguardManagerTest"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -357,7 +351,6 @@
"device-platinum-tests",
],
include_filters: ["android.app.PropertyInvalidatedCacheTests"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -372,7 +365,6 @@
"android.content.ComponentCallbacksControllerTest",
"android.content.ContextWrapperTest",
],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -383,7 +375,6 @@
"device-platinum-tests",
],
include_filters: ["android.database.sqlite.SQLiteRawStatementTest"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -395,7 +386,6 @@
],
include_filters: ["android.net"],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -508,7 +498,6 @@
"device-platinum-tests",
],
include_filters: ["com.android.internal.jank"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -567,7 +556,6 @@
"device-platinum-tests",
],
include_filters: ["com.android.internal.util.LatencyTrackerTest"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -578,7 +566,6 @@
"device-platinum-tests",
],
include_filters: ["android.content.ContentCaptureOptionsTest"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -600,7 +587,6 @@
],
include_filters: ["android.content.pm."],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -612,7 +598,6 @@
],
include_filters: ["android.content.pm."],
include_annotations: ["android.platform.test.annotations.Postsubmit"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -640,7 +625,6 @@
],
include_filters: ["android.content.res."],
include_annotations: ["android.platform.test.annotations.Postsubmit"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -670,7 +654,6 @@
"device-platinum-tests",
],
include_filters: ["android.view.contentcapture"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -681,7 +664,6 @@
"device-platinum-tests",
],
include_filters: ["android.view.contentprotection"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -693,7 +675,6 @@
],
include_filters: ["com.android.internal.content."],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -707,21 +688,6 @@
}
test_module_config {
- name: "FrameworksCoreTests_accessibility_NO_FLAKES",
- base: "FrameworksCoreTests",
- test_suites: [
- "device-tests",
- "device-platinum-tests",
- ],
- include_filters: [
- "com.android.internal.accessibility",
- "android.accessibilityservice",
- "android.view.accessibility",
- ],
- exclude_annotations: ["androidx.test.filters.FlakyTest"],
-}
-
-test_module_config {
name: "FrameworksCoreTests_accessibility",
base: "FrameworksCoreTests",
test_suites: [
@@ -790,7 +756,6 @@
"com.android.internal.jank.InteractionJankMonitorTest",
"com.android.internal.util.LatencyTrackerTest",
],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
@@ -801,7 +766,6 @@
"device-platinum-tests",
],
include_annotations: ["android.platform.test.annotations.PlatinumTest"],
- exclude_annotations: FLAKY_OR_IGNORED,
}
test_module_config {
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 99b73a4..b1f1e2c 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -22,6 +22,7 @@
<option name="test-file-name" value="FrameworksCoreTests.apk" />
<option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" />
<option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" />
+ <option name="test-file-name" value="BinderFrozenStateChangeCallbackTestApp.apk" />
<option name="test-file-name" value="BinderProxyCountingTestApp.apk" />
<option name="test-file-name" value="BinderProxyCountingTestService.apk" />
<option name="test-file-name" value="AppThatUsesAppOps.apk" />
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp
new file mode 100644
index 0000000..de97dda
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp
@@ -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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "BinderFrozenStateChangeCallbackTestApp",
+
+ static_libs: ["coretests-aidl"],
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..29c8f55
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.bfscctestapp">
+
+ <application>
+ <service android:name=".BfsccTestAppCmdService"
+ android:exported="true"/>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
new file mode 100644
index 0000000..77e8a40
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
@@ -0,0 +1,71 @@
+/*
+ * 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.frameworks.coretests.bfscctestapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.frameworks.coretests.aidl.IBfsccTestAppCmdService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class BfsccTestAppCmdService extends Service {
+ private IBfsccTestAppCmdService.Stub mBinder = new IBfsccTestAppCmdService.Stub() {
+ private final LinkedBlockingQueue<IBinder.IFrozenStateChangeCallback.State> mNotifications =
+ new LinkedBlockingQueue<>();
+
+ @Override
+ public void listenTo(IBinder binder) throws RemoteException {
+ binder.addFrozenStateChangeCallback(
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state)
+ -> mNotifications.offer(state));
+ }
+
+ @Override
+ public boolean[] waitAndConsumeNotifications() {
+ List<Boolean> results = new ArrayList<>();
+ try {
+ IBinder.IFrozenStateChangeCallback.State state =
+ mNotifications.poll(5, TimeUnit.SECONDS);
+ if (state != null) {
+ results.add(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ }
+ } catch (InterruptedException e) {
+ return null;
+ }
+ while (mNotifications.size() > 0) {
+ results.add(mNotifications.poll()
+ == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ }
+ boolean[] convertedResults = new boolean[results.size()];
+ for (int i = 0; i < results.size(); i++) {
+ convertedResults[i] = results.get(i).booleanValue();
+ }
+ return convertedResults;
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
index 41b4c69..09d79a6 100644
--- a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
@@ -50,4 +50,4 @@
public IBinder onBind(Intent intent) {
return mBinder;
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
similarity index 78%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
index 3c5beeb..d8d7dc4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.frameworks.coretests.aidl;
-parcelable BubbleBarLocation;
\ No newline at end of file
+interface IBfsccTestAppCmdService {
+ void listenTo(IBinder binder);
+ boolean[] waitAndConsumeNotifications();
+}
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index c08e42b..e19f887 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -47,12 +47,13 @@
import android.os.VibrationEffect;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.UsesFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.MediaStore.Audio.AudioColumns;
import android.test.mock.MockContentResolver;
import android.util.Xml;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.modules.utils.TypedXmlPullParser;
@@ -61,6 +62,7 @@
import com.google.common.base.Strings;
import org.junit.Before;
+import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,14 +73,28 @@
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.util.Arrays;
+import java.util.List;
import java.util.function.Consumer;
-@RunWith(AndroidJUnit4.class)
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
+@UsesFlags(android.app.Flags.class)
@SmallTest
@Presubmit
public class NotificationChannelTest {
+ @ClassRule
+ public static final SetFlagsRule.ClassRule mSetFlagsClassRule = new SetFlagsRule.ClassRule();
+
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_NOTIF_CHANNEL_CROP_VIBRATION_EFFECTS);
+ }
+
@Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public final SetFlagsRule mSetFlagsRule;
private final String CLASS = "android.app.NotificationChannel";
@@ -86,6 +102,10 @@
ContentProvider mContentProvider;
IContentProvider mIContentProvider;
+ public NotificationChannelTest(FlagsParameterization flags) {
+ mSetFlagsRule = mSetFlagsClassRule.createSetFlagsRule(flags);
+ }
+
@Before
public void setUp() throws Exception {
mContext = mock(Context.class);
@@ -233,6 +253,33 @@
}
@Test
+ @EnableFlags({Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API,
+ Flags.FLAG_NOTIF_CHANNEL_CROP_VIBRATION_EFFECTS})
+ public void testLongVibrationFields_canWriteToXml() throws Exception {
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ // populate pattern with contents
+ long[] pattern = new long[65550 / 2];
+ for (int i = 0; i < pattern.length; i++) {
+ pattern[i] = 100;
+ }
+ channel.setVibrationPattern(pattern); // with flag on, also sets effect
+
+ // Send it through parceling & unparceling to simulate being passed through a binder call
+ NotificationChannel fromParcel = writeToAndReadFromParcel(channel);
+ assertThat(fromParcel.getVibrationPattern().length).isEqualTo(
+ NotificationChannel.MAX_VIBRATION_LENGTH);
+
+ // Confirm that this also survives writing to & restoring from XML
+ NotificationChannel result = backUpAndRestore(fromParcel);
+ assertThat(result.getVibrationPattern().length).isEqualTo(
+ NotificationChannel.MAX_VIBRATION_LENGTH);
+ assertThat(result.getVibrationEffect()).isNotNull();
+ assertThat(result.getVibrationEffect()
+ .computeCreateWaveformOffOnTimingsOrNull())
+ .isEqualTo(result.getVibrationPattern());
+ }
+
+ @Test
public void testRestoreSoundUri_customLookup() throws Exception {
Uri uriToBeRestoredUncanonicalized = Uri.parse("content://media/1");
Uri uriToBeRestoredCanonicalized = Uri.parse("content://media/1?title=Song&canonical=1");
diff --git a/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java b/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java
index e19c4b1..3616ff5c 100644
--- a/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java
@@ -33,71 +33,169 @@
public class LauncherActivityInfoTest {
@Test
- public void testTrimStart() {
- // Invisible case
- assertThat(LauncherActivityInfo.trimStart("\u0009").toString()).isEmpty();
- // It is not supported in the system font
- assertThat(LauncherActivityInfo.trimStart("\u0FE1").toString()).isEmpty();
- // Surrogates case
- assertThat(LauncherActivityInfo.trimStart("\uD83E\uDD36").toString())
- .isEqualTo("\uD83E\uDD36");
- assertThat(LauncherActivityInfo.trimStart("\u0009\u0FE1\uD83E\uDD36A").toString())
- .isEqualTo("\uD83E\uDD36A");
- assertThat(LauncherActivityInfo.trimStart("\uD83E\uDD36A\u0009\u0FE1").toString())
- .isEqualTo("\uD83E\uDD36A\u0009\u0FE1");
- assertThat(LauncherActivityInfo.trimStart("A\uD83E\uDD36\u0009\u0FE1A").toString())
- .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
- assertThat(LauncherActivityInfo.trimStart(
- "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
- .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
- assertThat(LauncherActivityInfo.trimStart(
- "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
- .isEqualTo("\uD83E\uDD36A\u0009\u0FE1");
+ public void testIsVisible_normal() {
+ // normal
+ assertThat(LauncherActivityInfo.isVisible("label")).isTrue();
+ // 1 surrogates case
+ assertThat(LauncherActivityInfo.isVisible("\uD83E\uDD36")).isTrue();
}
@Test
- public void testTrimEnd() {
- // Invisible case
- assertThat(LauncherActivityInfo.trimEnd("\u0009").toString()).isEmpty();
- // It is not supported in the system font
- assertThat(LauncherActivityInfo.trimEnd("\u0FE1").toString()).isEmpty();
- // Surrogates case
- assertThat(LauncherActivityInfo.trimEnd("\uD83E\uDD36").toString())
- .isEqualTo("\uD83E\uDD36");
- assertThat(LauncherActivityInfo.trimEnd("\u0009\u0FE1\uD83E\uDD36A").toString())
- .isEqualTo("\u0009\u0FE1\uD83E\uDD36A");
- assertThat(LauncherActivityInfo.trimEnd("\uD83E\uDD36A\u0009\u0FE1").toString())
- .isEqualTo("\uD83E\uDD36A");
- assertThat(LauncherActivityInfo.trimEnd("A\uD83E\uDD36\u0009\u0FE1A").toString())
- .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
- assertThat(LauncherActivityInfo.trimEnd(
- "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
- .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
- assertThat(LauncherActivityInfo.trimEnd(
- "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
- .isEqualTo("\u0009\u0FE1\uD83E\uDD36A");
+ public void testIsVisible_onlyInvisibleCharacter() {
+ // 1 invisible
+ assertThat(LauncherActivityInfo.isVisible("\u0009")).isFalse();
+ // 2 invisible
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u3164")).isFalse();
+ // 3 invisible
+ assertThat(LauncherActivityInfo.isVisible("\u3000\u0009\u3164")).isFalse();
+ // 4 invisible
+ assertThat(LauncherActivityInfo.isVisible("\u200F\u3000\u0009\u3164")).isFalse();
}
@Test
- public void testTrim() {
- // Invisible case
- assertThat(LauncherActivityInfo.trim("\u0009").toString()).isEmpty();
- // It is not supported in the system font
- assertThat(LauncherActivityInfo.trim("\u0FE1").toString()).isEmpty();
- // Surrogates case
- assertThat(LauncherActivityInfo.trim("\uD83E\uDD36").toString())
- .isEqualTo("\uD83E\uDD36");
- assertThat(LauncherActivityInfo.trim("\u0009\u0FE1\uD83E\uDD36A").toString())
- .isEqualTo("\uD83E\uDD36A");
- assertThat(LauncherActivityInfo.trim("\uD83E\uDD36A\u0009\u0FE1").toString())
- .isEqualTo("\uD83E\uDD36A");
- assertThat(LauncherActivityInfo.trim("A\uD83E\uDD36\u0009\u0FE1A").toString())
- .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
- assertThat(LauncherActivityInfo.trim(
- "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
- .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
- assertThat(LauncherActivityInfo.trim(
- "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
- .isEqualTo("\uD83E\uDD36A");
+ public void testIsVisible_onlyNotSupportedCharacter() {
+ // 1 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1")).isFalse();
+ // 2 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2")).isFalse();
+ // 3 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2\u0FE3")).isFalse();
+ // 4 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();
+ }
+
+ @Test
+ public void testIsVisible_invisibleAndNotSupportedCharacter() {
+ // 1 invisible, 1 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1")).isFalse();
+ // 1 invisible, 2 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\u0FE2")).isFalse();
+ // 1 invisible, 3 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\u0FE2\u0FE3")).isFalse();
+ // 1 invisible, 4 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();
+
+ // 2 invisible, 1 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\u0FE1")).isFalse();
+ // 2 invisible, 2 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\u0FE1\u0FE2")).isFalse();
+ // 2 invisible, 3 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\u0FE1\u0FE2\u0FE3")).isFalse();
+ // 2 invisible, 4 not supported
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u0009\u3164\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();
+
+ // 3 invisible, 1 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u3000\u0009\u3164\u0FE1")).isFalse();
+ // 3 invisible, 2 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u3000\u0009\u3164\u0FE1\u0FE2")).isFalse();
+ // 3 invisible, 3 not supported
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3")).isFalse();
+ // 3 invisible, 4 not supported
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();
+
+ // 4 invisible, 1 not supported
+ assertThat(LauncherActivityInfo.isVisible("\u200F\u3000\u0009\u3164\u0FE1")).isFalse();
+ // 4 invisible, 2 not supported
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u200F\u3000\u0009\u3164\u0FE1\u0FE2")).isFalse();
+ // 4 invisible, 3 not supported
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3")).isFalse();
+ // 4 invisible, 4 not supported
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();
+
+ // 1 not supported, 1 invisible,
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0009")).isFalse();
+ // 1 not supported, 2 invisible
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0009\u3164")).isFalse();
+ // 1 not supported, 3 invisible
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u3000\u0009\u3164")).isFalse();
+ // 1 not supported, 4 invisible
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u200F\u3000\u0009\u3164")).isFalse();
+ }
+
+ @Test
+ public void testIsVisible_invisibleAndNormalCharacter() {
+ // 1 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0009\uD83E\uDD36")).isTrue();
+ // 2 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\uD83E\uDD36")).isTrue();
+ // 3 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u3000\u0009\u3164\uD83E\uDD36")).isFalse();
+ // 4 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u200F\u3000\u0009\u3164\uD83E\uDD36")).isFalse();
+ }
+
+ @Test
+ public void testIsVisible_notSupportedAndNormalCharacter() {
+ // 1 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\uD83E\uDD36")).isTrue();
+ // 2 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2\uD83E\uDD36")).isTrue();
+ // 3 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2\u0FE3\uD83E\uDD36")).isTrue();
+ // 4 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u0FE1\u0FE2\u0FE3\u0FE4\uD83E\uDD36")).isTrue();
+ }
+
+ @Test
+ public void testIsVisible_mixAllCharacter() {
+ // 1 invisible, 1 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\uD83E\uDD36")).isTrue();
+ // 1 invisible, 1 not supported, 1 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\u3164\uD83E\uDD36")).isTrue();
+ // 1 invisible, 1 not supported, 2 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u0009\u0FE1\u3000\u3164\uD83E\uDD36")).isTrue();
+ // 1 invisible, 1 not supported, 3 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u0009\u0FE1\u200F\u3000\u3164\uD83E\uDD36")).isTrue();
+
+ // 2 invisible, 1 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\u0FE1\uD83E\uDD36")).isTrue();
+ // 2 invisible, 2 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u0009\u3164\u0FE1\u0FE2\uD83E\uDD36")).isTrue();
+
+ // 3 invisible, 1 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u3000\u0009\u3164\u0FE1\uD83E\uDD36")).isFalse();
+ // 3 invisible, 2 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u3000\u0009\u3164\u0FE1\u0FE2\uD83E\uDD36")).isFalse();
+ // 3 invisible, 3 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\uD83E\uDD36")).isFalse();
+
+ // 4 invisible, 1 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u200F\u3000\u0009\u3164\u0FE1\uD83E\uDD36")).isFalse();
+ // 4 invisible, 2 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\uD83E\uDD36")).isFalse();
+ // 4 invisible, 3 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\uD83E\uDD36")).isFalse();
+ // 4 invisible, 4 not supported, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\u0FE4\uD83E\uDD36")).isFalse();
+
+ // 1 not supported, 1 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0009\uD83E\uDD36")).isTrue();
+ // 1 not supported, 2 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0009\u3164\uD83E\uDD36")).isTrue();
+ // 1 not supported, 3 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u0FE1\u3000\u0009\u3164\uD83E\uDD36")).isTrue();
+ // 1 not supported, 4 invisible, 1 surrogates
+ assertThat(LauncherActivityInfo.isVisible(
+ "\u0FE1\u200F\u3000\u0009\u3164\uD83E\uDD36")).isTrue();
+
}
}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 519f23b..00978a0 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -538,4 +538,58 @@
assertEquals(1, db.mConnection.size());
}
+
+ // Create and open the database, allowing or disallowing double-quoted strings.
+ private void createDatabase(boolean noDoubleQuotedStrs) throws Exception {
+ // The open-flags that do not change in this test.
+ int flags = SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.OPEN_READWRITE;
+
+ // The flag to be tested.
+ int flagUnderTest = SQLiteDatabase.NO_DOUBLE_QUOTED_STRS;
+
+ if (noDoubleQuotedStrs) {
+ flags |= flagUnderTest;
+ } else {
+ flags &= ~flagUnderTest;
+ }
+ mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), null, flags, null);
+ }
+
+ /**
+ * This test verifies that the NO_DOUBLE_QUOTED_STRS flag works as expected when opening a
+ * database. This does not test that the flag is initialized as expected from the system
+ * properties.
+ */
+ @Test
+ public void testNoDoubleQuotedStrings() throws Exception {
+ closeAndDeleteDatabase();
+ createDatabase(/* noDoubleQuotedStrs */ false);
+
+ mDatabase.beginTransaction();
+ try {
+ mDatabase.execSQL("CREATE TABLE t1 (t text);");
+ // Insert a value in double-quotes. This is invalid but accepted.
+ mDatabase.execSQL("INSERT INTO t1 (t) VALUES (\"foo\")");
+ } finally {
+ mDatabase.endTransaction();
+ }
+
+ closeAndDeleteDatabase();
+ createDatabase(/* noDoubleQuotedStrs */ true);
+
+ mDatabase.beginTransaction();
+ try {
+ mDatabase.execSQL("CREATE TABLE t1 (t text);");
+ try {
+ // Insert a value in double-quotes. This is invalid and must throw.
+ mDatabase.execSQL("INSERT INTO t1 (t) VALUES (\"foo\")");
+ fail("expected an exception");
+ } catch (SQLiteException e) {
+ assertTrue(e.toString().contains("no such column"));
+ }
+ } finally {
+ mDatabase.endTransaction();
+ }
+ closeAndDeleteDatabase();
+ }
}
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 10aed8d..1429272 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -16,8 +16,6 @@
package android.graphics;
-import static com.android.text.flags.Flags.FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -32,7 +30,6 @@
import android.graphics.fonts.SystemFonts;
import android.graphics.text.PositionedGlyphs;
import android.graphics.text.TextRunShaper;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.FontConfig;
@@ -931,7 +928,6 @@
return String.format(xml, op, lang, font);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_prepend() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
@@ -947,7 +943,6 @@
assertB3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_replace() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
@@ -963,7 +958,6 @@
assertB3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_append() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
@@ -979,7 +973,6 @@
assertA3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_ScriptMismatch() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
@@ -995,7 +988,6 @@
assertA3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_SubscriptMatch() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
new file mode 100644
index 0000000..ee2e7e0
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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 android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.frameworks.coretests.aidl.IBfsccTestAppCmdService;
+import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests functionality of {@link android.os.IBinder.IFrozenStateChangeCallback}.
+ */
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+public class BinderFrozenStateChangeNotificationTest {
+ private static final String TAG = BinderFrozenStateChangeNotificationTest.class.getSimpleName();
+
+ private static final String TEST_PACKAGE_NAME_1 =
+ "com.android.frameworks.coretests.bfscctestapp";
+ private static final String TEST_PACKAGE_NAME_2 =
+ "com.android.frameworks.coretests.bdr_helper_app1";
+ private static final String TEST_APP_CMD_SERVICE =
+ TEST_PACKAGE_NAME_1 + ".BfsccTestAppCmdService";
+
+ private static final int CALLBACK_WAIT_TIMEOUT_SECS = 5;
+
+ private IBfsccTestAppCmdService mBfsccTestAppCmdService;
+ private ServiceConnection mTestAppConnection;
+ private Context mContext;
+ private Handler mHandler;
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mHandler = new Handler(Looper.getMainLooper());
+ ((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE)).killUid(
+ mContext.getPackageManager().getPackageUid(TEST_PACKAGE_NAME_1, 0),
+ "Wiping Test Package");
+ mTestAppConnection = bindService();
+ }
+
+ private IBinder getNewRemoteBinder(String testPackage) throws InterruptedException {
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ final AtomicInteger resultCode = new AtomicInteger(Activity.RESULT_CANCELED);
+ final AtomicReference<Bundle> resultExtras = new AtomicReference<>();
+
+ final Intent intent = new Intent(TestCommsReceiver.ACTION_GET_BINDER)
+ .setClassName(testPackage, TestCommsReceiver.class.getName());
+ mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ resultCode.set(getResultCode());
+ resultExtras.set(getResultExtras(true));
+ resultLatch.countDown();
+ }
+ }, mHandler, Activity.RESULT_CANCELED, null, null);
+
+ assertTrue("Request for binder timed out", resultLatch.await(5, TimeUnit.SECONDS));
+ assertEquals(Activity.RESULT_OK, resultCode.get());
+ return resultExtras.get().getBinder(TestCommsReceiver.EXTRA_KEY_BINDER);
+ }
+
+ private ServiceConnection bindService()
+ throws Exception {
+ final CountDownLatch bindLatch = new CountDownLatch(1);
+ ServiceConnection connection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "Service connected");
+ mBfsccTestAppCmdService = IBfsccTestAppCmdService.Stub.asInterface(service);
+ bindLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "Service disconnected");
+ }
+ };
+ mContext.bindService(
+ new Intent().setComponent(
+ new ComponentName(TEST_PACKAGE_NAME_1, TEST_APP_CMD_SERVICE)),
+ connection,
+ Context.BIND_AUTO_CREATE
+ | Context.BIND_NOT_FOREGROUND);
+ if (!bindLatch.await(5, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for the service to bind");
+ }
+ return connection;
+ }
+
+ private void unbindService(ServiceConnection service) {
+ if (service != null) {
+ mContext.unbindService(service);
+ }
+ }
+
+ @Test
+ public void onStateChangeCalled() throws Exception {
+ final LinkedBlockingQueue<Boolean> results = new LinkedBlockingQueue<>();
+ if (createCallback(mBfsccTestAppCmdService.asBinder(), results) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results);
+ freezeApp1();
+ ensureFrozenCallback(results);
+ unfreezeApp1();
+ ensureUnfrozenCallback(results);
+ }
+
+ @Test
+ public void onStateChangeNotCalledAfterCallbackRemoved() throws Exception {
+ final LinkedBlockingQueue<Boolean> results = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback;
+ if ((callback = createCallback(mBfsccTestAppCmdService.asBinder(), results)) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results);
+ mBfsccTestAppCmdService.asBinder().removeFrozenStateChangeCallback(callback);
+ freezeApp1();
+ assertEquals("No more callbacks should be invoked.", 0, results.size());
+ }
+
+ @Test
+ public void multipleCallbacks() throws Exception {
+ final LinkedBlockingQueue<Boolean> results1 = new LinkedBlockingQueue<>();
+ final LinkedBlockingQueue<Boolean> results2 = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback1;
+ if ((callback1 = createCallback(mBfsccTestAppCmdService.asBinder(), results1)) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results1);
+ freezeApp1();
+ ensureFrozenCallback(results1);
+ if (createCallback(mBfsccTestAppCmdService.asBinder(), results2) == null) {
+ return;
+ }
+ ensureFrozenCallback(results2);
+
+ unfreezeApp1();
+ ensureUnfrozenCallback(results1);
+ ensureUnfrozenCallback(results2);
+
+ mBfsccTestAppCmdService.asBinder().removeFrozenStateChangeCallback(callback1);
+ freezeApp1();
+ assertEquals("No more callbacks should be invoked.", 0, results1.size());
+ ensureFrozenCallback(results2);
+ }
+
+ @Test
+ public void onStateChangeCalledWithTheRightBinder() throws Exception {
+ final IBinder binder = mBfsccTestAppCmdService.asBinder();
+ final LinkedBlockingQueue<IBinder> results = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback =
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state) -> results.offer(who);
+ try {
+ binder.addFrozenStateChangeCallback(callback);
+ } catch (UnsupportedOperationException e) {
+ return;
+ }
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ freezeApp1();
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ unfreezeApp1();
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ @After
+ public void tearDown() {
+ if (mTestAppConnection != null) {
+ mContext.unbindService(mTestAppConnection);
+ }
+ }
+
+ private IBinder.IFrozenStateChangeCallback createCallback(IBinder binder, Queue<Boolean> queue)
+ throws RemoteException {
+ try {
+ final IBinder.IFrozenStateChangeCallback callback =
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state) ->
+ queue.offer(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ binder.addFrozenStateChangeCallback(callback);
+ return callback;
+ } catch (UnsupportedOperationException e) {
+ return null;
+ }
+ }
+
+ private void ensureFrozenCallback(LinkedBlockingQueue<Boolean> queue)
+ throws InterruptedException {
+ assertEquals(Boolean.TRUE, queue.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ private void ensureUnfrozenCallback(LinkedBlockingQueue<Boolean> queue)
+ throws InterruptedException {
+ assertEquals(Boolean.FALSE, queue.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ private String executeShellCommand(String cmd) throws Exception {
+ return UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+ }
+
+ private void freezeApp1() throws Exception {
+ executeShellCommand("am freeze " + TEST_PACKAGE_NAME_1);
+ }
+
+ private void freezeApp2() throws Exception {
+ executeShellCommand("am freeze " + TEST_PACKAGE_NAME_2);
+ }
+
+ private void unfreezeApp1() throws Exception {
+ executeShellCommand("am unfreeze " + TEST_PACKAGE_NAME_1);
+ }
+
+ private void unfreezeApp2() throws Exception {
+ executeShellCommand("am unfreeze " + TEST_PACKAGE_NAME_2);
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java
index 9767d67..90ec93e 100644
--- a/core/tests/coretests/src/android/os/BinderTest.java
+++ b/core/tests/coretests/src/android/os/BinderTest.java
@@ -24,18 +24,16 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.testng.Assert.assertThrows;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import com.android.internal.os.BinderInternal;
-
import org.junit.Rule;
import org.junit.Test;
-@IgnoreUnderRavenwood(blockedBy = WorkSource.class)
public class BinderTest {
private static final int UID = 100;
@@ -89,6 +87,7 @@
@SmallTest
@Test(expected = java.lang.SecurityException.class)
+ @DisabledOnRavenwood(blockedBy = ServiceManagerNative.class)
public void testServiceManagerNativeSecurityException() throws RemoteException {
// Find the service manager
IServiceManager sServiceManager = ServiceManagerNative
@@ -101,6 +100,7 @@
@SmallTest
@Test(expected = java.lang.NullPointerException.class)
+ @DisabledOnRavenwood(blockedBy = ServiceManagerNative.class)
public void testServiceManagerNativeNullptrException() throws RemoteException {
// Find the service manager
IServiceManager sServiceManager = ServiceManagerNative
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index ded6fc5..31e0752 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -25,7 +25,6 @@
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
@@ -131,7 +130,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class)
public void testCreateFromParcel() throws Exception {
boolean withFd;
Parcel p;
@@ -312,7 +310,7 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
+ @DisabledOnRavenwood(reason = "Ravenwood tests run on the BCP")
public void kindofEquals_lazyValuesAndDifferentClassLoaders_returnsFalse() {
Parcelable p1 = new CustomParcelable(13, "Tiramisu");
Parcelable p2 = new CustomParcelable(13, "Tiramisu");
@@ -368,7 +366,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void readWriteLengthMismatch_logsWtf() throws Exception {
mWtfHandler = Log.setWtfHandler((tag, e, system) -> {
throw new RuntimeException(e);
@@ -383,7 +380,7 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
+ @DisabledOnRavenwood(reason = "Ravenwood tests run on the BCP")
public void getParcelable_whenThrowingAndNotDefusing_throws() throws Exception {
Bundle.setShouldDefuse(false);
Bundle bundle = new Bundle();
@@ -396,7 +393,7 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
+ @DisabledOnRavenwood(reason = "Ravenwood tests run on the BCP")
public void getParcelable_whenThrowingAndDefusing_returnsNull() throws Exception {
Bundle.setShouldDefuse(true);
Bundle bundle = new Bundle();
@@ -412,7 +409,7 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
+ @DisabledOnRavenwood(reason = "Ravenwood tests run on the BCP")
public void getParcelable_whenThrowingAndDefusing_leavesElement() throws Exception {
Bundle.setShouldDefuse(true);
Bundle bundle = new Bundle();
@@ -447,7 +444,6 @@
}
@Test
- @DisabledOnRavenwood(blockedBy = Parcel.class)
public void parcelledBundleWithBinder_shouldReturnHasBindersTrue() throws Exception {
Bundle bundle = new Bundle();
bundle.putParcelable("test", new CustomParcelable(13, "Tiramisu"));
@@ -470,7 +466,6 @@
}
@Test
- @DisabledOnRavenwood(blockedBy = Parcel.class)
public void parcelledBundleWithoutBinder_shouldReturnHasBindersFalse() throws Exception {
Bundle bundle = new Bundle();
bundle.putParcelable("test", new CustomParcelable(13, "Tiramisu"));
diff --git a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
index 09395f1..96316c4 100644
--- a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
+++ b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
@@ -20,7 +20,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArrayMap;
@@ -67,7 +67,7 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
+ @DisabledOnRavenwood(blockedBy = android.text.Spanned.class)
public void nullCharSequence() {
Parcel p = Parcel.obtain();
p.writeCharSequence(null);
@@ -76,7 +76,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void nullStrongBinder() {
Parcel p = Parcel.obtain();
p.writeStrongBinder(null);
@@ -85,7 +84,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void nullStringInterface() {
Parcel p = Parcel.obtain();
p.writeStrongInterface(null);
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 0373231..da9d687 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
@@ -48,7 +47,6 @@
private static final String INTERFACE_TOKEN_2 = "Another IBinder interface token";
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testIsForRpc() {
Parcel p = Parcel.obtain();
assertEquals(false, p.isForRpc());
@@ -56,7 +54,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCallingWorkSourceUidAfterWrite() {
Parcel p = Parcel.obtain();
// Method does not throw if replaceCallingWorkSourceUid is called before requests headers
@@ -77,7 +74,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCallingWorkSourceUidAfterEnforce() {
Parcel p = Parcel.obtain();
p.writeInterfaceToken(INTERFACE_TOKEN_1);
@@ -95,7 +91,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testParcelWithMultipleHeaders() {
Parcel p = Parcel.obtain();
Binder.setCallingWorkSourceUid(WORK_SOURCE_1);
@@ -153,7 +148,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenSameDataWithBinder() {
Binder binder = new Binder();
Parcel pA = Parcel.obtain();
@@ -313,7 +307,6 @@
* and 1M length for complex objects are allowed.
*/
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testAllocations_whenWithinLimit() {
Binder.setIsDirectlyHandlingTransactionOverride(true);
Parcel p = Parcel.obtain();
@@ -398,7 +391,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testHasBinders_AfterWritingBinderToParcel() {
Binder binder = new Binder();
Parcel pA = Parcel.obtain();
@@ -410,7 +402,6 @@
}
@Test
- @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testHasBindersInRange_AfterWritingBinderToParcel() {
Binder binder = new Binder();
Parcel pA = Parcel.obtain();
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index ecd2f76..b157c95 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -16,8 +16,11 @@
package android.os.storage;
+import android.content.res.ObbInfo;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ProxyFileDescriptorCallback;
+import android.os.ServiceManager;
import android.system.ErrnoException;
import androidx.test.filters.LargeTest;
@@ -104,7 +107,14 @@
public void testMountBadPackageNameObb() throws Exception {
final File file = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
String filePath = file.getAbsolutePath();
- mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ try {
+ mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ fail("mountObb should throw an exception as package name is incorrect");
+ } catch (Exception ex) {
+ assertEquals("Path " + filePath
+ + " does not contain package name " + mContext.getPackageName(),
+ ex.getMessage());
+ }
}
/**
@@ -154,6 +164,48 @@
}
}
+ @LargeTest
+ public void testObbInfo_withValidObbInfo_success() throws Exception {
+ final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+ String filePath = file.getAbsolutePath();
+ try {
+ mountObb(filePath);
+ unmountObb(filePath, DONT_FORCE);
+ } catch (Exception ex) {
+ fail("No exception expected, got " + ex.getMessage());
+ }
+ }
+
+ @LargeTest
+ public void testObbInfo_withInvalidObbInfo_exception() throws Exception {
+ final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+ String rawPath = file.getAbsolutePath();
+ String canonicalPath = file.getCanonicalPath();
+
+ ObbInfo obbInfo = ObbInfo.CREATOR.createFromParcel(Parcel.obtain());
+ obbInfo.packageName = "com.android.obbcrash";
+ obbInfo.version = 1;
+ obbInfo.filename = canonicalPath;
+
+ try {
+ IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")).mountObb(
+ rawPath, canonicalPath, new ObbActionListener(), 0, obbInfo);
+ fail("mountObb should throw an exception as package name is incorrect");
+ } catch (SecurityException ex) {
+ assertEquals("Path " + canonicalPath
+ + " does not contain package name " + mContext.getPackageName(),
+ ex.getMessage());
+ }
+ }
+
+ private static class ObbActionListener extends IObbActionListener.Stub {
+ @SuppressWarnings("hiding")
+ @Override
+ public void onObbResult(String filename, int nonce, int status) {
+
+ }
+ }
+
private static class MyThreadFactory implements ThreadFactory {
Thread thread = null;
diff --git a/core/tests/coretests/src/android/text/TextLineLetterSpacingTest.kt b/core/tests/coretests/src/android/text/TextLineLetterSpacingTest.kt
index 71980c1..e4e04a0 100644
--- a/core/tests/coretests/src/android/text/TextLineLetterSpacingTest.kt
+++ b/core/tests/coretests/src/android/text/TextLineLetterSpacingTest.kt
@@ -17,11 +17,9 @@
package android.text
import android.graphics.Paint
-import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -40,7 +38,6 @@
@JvmField
val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
- @RequiresFlagsEnabled(FLAG_LETTER_SPACING_JUSTIFICATION)
@Test
fun calculateRunFlagTest() {
// Only one Bidi run
@@ -84,7 +81,6 @@
.isEqualTo(LEFT_EDGE)
}
- @RequiresFlagsEnabled(FLAG_LETTER_SPACING_JUSTIFICATION)
@Test
fun resolveRunFlagForSubSequenceTest() {
val runStart = 5
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 4d9b591c..00ffda8 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -254,11 +254,8 @@
float progress = 0.5f;
mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
// verify correct ime insets manipulation
- float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
- int expectedInset =
- (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
- eq(Insets.of(0, 0, 0, expectedInset)), eq(1f), anyFloat());
+ eq(Insets.of(0, 0, 0, getImeHeight(progress))), eq(1f), anyFloat());
}
@Test
@@ -268,12 +265,13 @@
WindowInsetsAnimationControlListener animationControlListener = startBackGesture();
// progress back gesture
- mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
+ float progress = 0.5f;
+ mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
// commit back gesture
mBackAnimationController.onBackInvoked();
- // verify setInsetsAndAlpha never called due onReady delayed
+ // verify setInsetsAndAlpha never called due to onReady delayed
verify(mWindowInsetsAnimationController, never()).setInsetsAndAlpha(any(), anyInt(),
anyFloat());
verify(mInsetsController, never()).setPredictiveBackImeHideAnimInProgress(eq(true));
@@ -283,7 +281,7 @@
// verify setInsetsAndAlpha immediately called
verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
- eq(Insets.of(0, 0, 0, IME_HEIGHT)), eq(1f), anyFloat());
+ eq(Insets.of(0, 0, 0, getImeHeight(progress))), eq(1f), anyFloat());
// verify post-commit hide anim has started
verify(mInsetsController, times(1)).setPredictiveBackImeHideAnimInProgress(eq(true));
});
@@ -319,4 +317,9 @@
return animationControlListener.getValue();
}
+
+ private int getImeHeight(float gestureProgress) {
+ float interpolatedProgress = BACK_GESTURE.getInterpolation(gestureProgress);
+ return (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index ce7e858..bec8b1f 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -1022,7 +1022,7 @@
}
@Test
- public void testImeRequestedVisibleDuringPredictiveBackAnim() {
+ public void testImeRequestedVisibleDuringPredictiveBackAnimWithoutCallback() {
prepareControls();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// show ime as initial state
@@ -1051,6 +1051,42 @@
}
@Test
+ public void testImeRequestedInvisibleDuringPredictiveBackAnimWithCallback() {
+ prepareControls();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // set WindowInsetsAnimationCallback on ViewRoot
+ mViewRoot.getView().setWindowInsetsAnimationCallback(
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @Override
+ public WindowInsets onProgress(
+ @NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return insets;
+ }
+ });
+
+ // show ime as initial state
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+ mController.cancelExistingAnimations(); // fast forward show animation
+ assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+ // start control request (for predictive back animation)
+ WindowInsetsAnimationControlListener listener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+ listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+ ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+ // Verify that onReady is called (after next predraw)
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+ verify(listener).onReady(notNull(), eq(ime()));
+
+ // verify that insets are requested invisible during animation
+ assertFalse(isRequestedVisible(mController, ime()));
+ });
+ }
+
+ @Test
public void testImeShowRequestCancelsPredictiveBackPostCommitAnim() {
prepareControls();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -1131,6 +1167,37 @@
});
}
+ @Test
+ public void testPredictiveBackControlRequestCancelledDuringImeHideAnim() {
+ prepareControls();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // show ime as initial state
+ if (!Flags.refactorInsetsController()) {
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+ } else {
+ mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+ }
+ mController.cancelExistingAnimations(); // fast forward show animation
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+ assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+ // start IME hide animation
+ mController.hide(ime(), true /* fromIme */, null /* statsToken */);
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ime()));
+
+ // start control request (for predictive back animation)
+ WindowInsetsAnimationControlListener listener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+ listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+ ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+ // verify that control request is cancelled and animation type remains HIDE
+ verify(listener).onCancelled(any());
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ime()));
+ });
+ }
+
private void waitUntilNextFrame() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java b/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
deleted file mode 100644
index 7c14032..0000000
--- a/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
+++ /dev/null
@@ -1,155 +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 android.widget;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import android.content.Context;
-import android.util.SizeF;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-import android.view.View;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.frameworks.coretests.R;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-
-import java.util.Map;
-
-/**
- * Tests for RemoteViews.
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class RemoteViewsProtoTest {
-
- // This can point to any other package which exists on the device.
- private static final String OTHER_PACKAGE = "com.android.systemui";
-
- @Rule
- public final ExpectedException exception = ExpectedException.none();
-
- private Context mContext;
- private String mPackage;
- private LinearLayout mContainer;
-
- @Before
- public void setup() {
- mContext = InstrumentationRegistry.getContext();
- mPackage = mContext.getPackageName();
- mContainer = new LinearLayout(mContext);
- }
-
- @Test
- public void copy_canStillBeApplied() {
- RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
-
- RemoteViews clone = recreateFromProto(original);
-
- clone.apply(mContext, mContainer);
- }
-
- @SuppressWarnings("ReturnValueIgnored")
- @Test
- public void clone_repeatedly() {
- RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
-
- recreateFromProto(original);
- recreateFromProto(original);
-
- original.apply(mContext, mContainer);
- }
-
- @Test
- public void clone_chained() {
- RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
-
- RemoteViews clone = recreateFromProto(recreateFromProto(original));
-
-
- clone.apply(mContext, mContainer);
- }
-
- @Test
- public void landscapePortraitViews_lightBackgroundLayoutFlag() {
- RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
- inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
-
- RemoteViews parent = new RemoteViews(inner, inner);
- parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
-
- View view = recreateFromProto(parent).apply(mContext, mContainer);
- assertNull(view.findViewById(R.id.text));
- assertNotNull(view.findViewById(R.id.light_background_text));
- }
-
- @Test
- public void sizedViews_lightBackgroundLayoutFlag() {
- RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
- inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
-
- RemoteViews parent = new RemoteViews(
- Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
- parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
-
- View view = recreateFromProto(parent).apply(mContext, mContainer);
- assertNull(view.findViewById(R.id.text));
- assertNotNull(view.findViewById(R.id.light_background_text));
- }
-
- @Test
- public void nestedLandscapeViews() throws Exception {
- RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
- for (int i = 0; i < 10; i++) {
- views = new RemoteViews(views, new RemoteViews(mPackage, R.layout.remote_views_test));
- }
- // writeTo/createFromProto works
- recreateFromProto(views);
-
- views = new RemoteViews(mPackage, R.layout.remote_views_test);
- for (int i = 0; i < 11; i++) {
- views = new RemoteViews(views, new RemoteViews(mPackage, R.layout.remote_views_test));
- }
- // writeTo/createFromProto fails
- exception.expect(IllegalArgumentException.class);
- recreateFromProtoNoRethrow(views);
- }
-
- private RemoteViews recreateFromProto(RemoteViews views) {
- try {
- return recreateFromProtoNoRethrow(views);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private RemoteViews recreateFromProtoNoRethrow(RemoteViews views) throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- views.writePreviewToProto(mContext, out);
- ProtoInputStream in = new ProtoInputStream(out.getBytes());
- return RemoteViews.createPreviewFromProto(mContext, in);
- }
-}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index d153edd..46dfcb5 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -44,6 +44,7 @@
import android.view.ImeBackAnimationController;
import android.view.MotionEvent;
+import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -61,6 +62,10 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Tests for {@link WindowOnBackInvokedDispatcherTest}
@@ -117,6 +122,8 @@
mDispatcher = new WindowOnBackInvokedDispatcher(mContext, Looper.getMainLooper());
mDispatcher.attachToWindow(mWindowSession, mWindow, null, mImeBackAnimationController);
+ clearInvocations(mCallback1);
+ clearInvocations(mCallback2);
}
private void waitForIdle() {
@@ -472,6 +479,102 @@
verifyImeCallackRegistrations();
}
+ @Test
+ public void onBackInvoked_notCalledAfterCallbackUnregistration()
+ throws RemoteException, InterruptedException {
+ // Setup a callback that unregisters itself after the gesture is finished but before the
+ // fling animation has ended
+ final AtomicBoolean unregisterOnProgressUpdate = new AtomicBoolean(false);
+ final AtomicInteger onBackInvokedCalled = new AtomicInteger(0);
+ final CountDownLatch onBackCancelledCalled = new CountDownLatch(1);
+ OnBackAnimationCallback onBackAnimationCallback = new OnBackAnimationCallback() {
+ @Override
+ public void onBackProgressed(@NonNull BackEvent backEvent) {
+ if (unregisterOnProgressUpdate.get()) {
+ mDispatcher.unregisterOnBackInvokedCallback(this);
+ }
+ }
+
+ @Override
+ public void onBackInvoked() {
+ onBackInvokedCalled.getAndIncrement();
+ }
+
+ @Override
+ public void onBackCancelled() {
+ onBackCancelledCalled.countDown();
+ }
+ };
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, onBackAnimationCallback);
+ OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
+
+ callbackInfo.getCallback().onBackStarted(mBackEvent);
+ waitForIdle();
+ assertTrue(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
+
+ // simulate back gesture finished and onBackInvoked() called, which starts the fling slow
+ // down animation. By setting unregisterOnProgressUpdate to true, the callback will
+ // unregister itself as soon as it receives the first progress event (coming from the
+ // generated fling slow down events)
+ unregisterOnProgressUpdate.set(true);
+ callbackInfo.getCallback().onBackInvoked();
+ waitForIdle();
+ onBackCancelledCalled.await(1000, TimeUnit.MILLISECONDS);
+
+ // verify that onBackCancelled is called in this case instead of onBackInvoked
+ assertEquals(0, onBackCancelledCalled.getCount());
+ assertEquals(0, onBackInvokedCalled.get());
+ verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), isNull());
+ assertFalse(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
+ }
+
+ @Test
+ public void onBackCancelled_calledOnceAfterCallbackUnregistration()
+ throws RemoteException, InterruptedException {
+ // Setup a callback that unregisters itself after the gesture is finished but before the
+ // progress is animated back to 0f
+ final AtomicBoolean unregisterOnProgressUpdate = new AtomicBoolean(false);
+ final AtomicInteger onBackInvokedCalled = new AtomicInteger(0);
+ final CountDownLatch onBackCancelledCalled = new CountDownLatch(1);
+ OnBackAnimationCallback onBackAnimationCallback = new OnBackAnimationCallback() {
+ @Override
+ public void onBackProgressed(@NonNull BackEvent backEvent) {
+ if (unregisterOnProgressUpdate.get()) {
+ mDispatcher.unregisterOnBackInvokedCallback(this);
+ }
+ }
+
+ @Override
+ public void onBackInvoked() {
+ onBackInvokedCalled.getAndIncrement();
+ }
+
+ @Override
+ public void onBackCancelled() {
+ onBackCancelledCalled.countDown();
+ }
+ };
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, onBackAnimationCallback);
+ OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
+
+ callbackInfo.getCallback().onBackStarted(mBackEvent);
+ waitForIdle();
+ assertTrue(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
+
+ // simulate back gesture finished and onBackCancelled() called, which starts the progress
+ // animation back to 0f. On the first progress emission, the callback will unregister itself
+ unregisterOnProgressUpdate.set(true);
+ callbackInfo.getCallback().onBackCancelled();
+ waitForIdle();
+ onBackCancelledCalled.await(1000, TimeUnit.MILLISECONDS);
+
+ // verify that onBackCancelled is called exactly once in this case
+ assertEquals(0, onBackCancelledCalled.getCount());
+ assertEquals(0, onBackInvokedCalled.get());
+ verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), isNull());
+ assertFalse(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
+ }
+
private void verifyImeCallackRegistrations() throws RemoteException {
// verify default callback is replaced with ImeBackAnimationController
mDispatcher.registerOnBackInvokedCallbackUnchecked(mDefaultImeCallback, PRIORITY_DEFAULT);
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 66de3d7..397cdcf 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -124,6 +124,16 @@
return this;
}
+ @Override
+ public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ }
+
+ @Override
+ public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+ return false;
+ }
+
public void die() {
isAlive = false;
if (mRecipient != null) {
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
index b183ecb..149e132 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
@@ -20,6 +20,7 @@
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.UserHandle;
@@ -69,22 +70,22 @@
assertThat(copy.preloadedIcon).isEqualTo(original.preloadedIcon);
}
-
private static StatusBarIcon newStatusBarIcon() {
final UserHandle dummyUserHandle = UserHandle.of(100);
final String dummyIconPackageName = "com.android.internal.statusbar.test";
- final int dummyIconId = 123;
+ final Icon dummyIcon = Icon.createWithResource(dummyIconPackageName, 123);
final int dummyIconLevel = 1;
final int dummyIconNumber = 2;
final CharSequence dummyIconContentDescription = "dummyIcon";
return new StatusBarIcon(
- dummyIconPackageName,
dummyUserHandle,
- dummyIconId,
+ dummyIconPackageName,
+ dummyIcon,
dummyIconLevel,
dummyIconNumber,
dummyIconContentDescription,
- StatusBarIcon.Type.SystemIcon);
+ StatusBarIcon.Type.SystemIcon,
+ StatusBarIcon.Shape.FIXED_SPACE);
}
private static void assertSerializableFieldsEqual(StatusBarIcon copy, StatusBarIcon original) {
@@ -96,6 +97,7 @@
assertThat(copy.number).isEqualTo(original.number);
assertThat(copy.contentDescription).isEqualTo(original.contentDescription);
assertThat(copy.type).isEqualTo(original.type);
+ assertThat(copy.shape).isEqualTo(original.shape);
}
private static StatusBarIcon parcelAndUnparcel(StatusBarIcon original) {
diff --git a/core/tests/coretests/src/com/android/internal/widget/ViewGroupFaderTest.java b/core/tests/coretests/src/com/android/internal/widget/ViewGroupFaderTest.java
new file mode 100644
index 0000000..eeabc2f
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/ViewGroupFaderTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.widget;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.test.AndroidTestCase;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.flags.Flags;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link ViewGroupFader}.
+ */
+public class ViewGroupFaderTest extends AndroidTestCase {
+
+ private Context mContext;
+ private ViewGroupFader mViewGroupFader;
+ private Resources mResources;
+
+ @Mock
+ private ViewGroup mViewGroup,mViewGroup1;
+
+ @Mock
+ private ViewGroupFader mockViewGroupFader;
+
+ @Mock
+ private ViewGroupFader.AnimationCallback mAnimationCallback;
+
+ @Mock
+ private ViewGroupFader.ChildViewBoundsProvider mChildViewBoundsProvider;
+
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final Context mContext = getInstrumentation().getContext();
+ mResources = spy(mContext.getResources());
+ when(mResources.getBoolean(com.android.internal.R.bool.config_enableViewGroupScalingFading))
+ .thenReturn(true);
+ when(mViewGroup.getResources()).thenReturn(mResources);
+
+ mViewGroupFader = new ViewGroupFader(
+ mViewGroup,
+ mAnimationCallback,
+ mChildViewBoundsProvider);
+ }
+
+ /** This test checks that for each child of the parent viewgroup,
+ * updateListElementFades is called for each of its child, when the Flag is set to true
+ */
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FADING_VIEW_GROUP)
+ public void testFadingAndScrollingAnimationWorking_FlagOn() {
+ mViewGroup.addView(mViewGroup1);
+ mViewGroupFader.updateFade();
+
+ for (int i = 0; i < mViewGroup.getChildCount(); i++) {
+ View child = mViewGroup.getChildAt(i);
+ verify(mockViewGroupFader).updateListElementFades((ViewGroup)child,true);
+ }
+ }
+
+ /** This test checks that for each child of the parent viewgroup,
+ * updateListElementFades is never called for each of its child, when the Flag is set to false
+ */
+ @Test
+ public void testFadingAndScrollingAnimationNotWorking_FlagOff() {
+ mViewGroup.addView(mViewGroup1);
+ mViewGroupFader.updateFade();
+
+ for (int i = 0; i < mViewGroup.getChildCount(); i++) {
+ View child = mViewGroup.getChildAt(i);
+ verify(mockViewGroupFader,never()).updateListElementFades((ViewGroup)child,true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
index 88f24d7..7a5757c 100644
--- a/core/tests/hdmitests/Android.bp
+++ b/core/tests/hdmitests/Android.bp
@@ -37,3 +37,10 @@
certificate: "platform",
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "HdmiCecTests_hardware_hdmi",
+ base: "HdmiCecTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.hardware.hdmi"],
+}
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index 8301a7f..8657b8c 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -63,3 +63,13 @@
certificate: "platform",
}
+
+test_module_config {
+ name: "FrameworksMockingCoreTests_os_bundlerecyclingtest",
+ base: "FrameworksMockingCoreTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["android.os.BundleRecyclingTest"],
+}
diff --git a/core/tests/timetests/Android.bp b/core/tests/timetests/Android.bp
index 89dade7..04bbe69 100644
--- a/core/tests/timetests/Android.bp
+++ b/core/tests/timetests/Android.bp
@@ -23,3 +23,17 @@
certificate: "platform",
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "FrameworksTimeCoreTests_android_app",
+ base: "FrameworksTimeCoreTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.app."],
+}
+
+test_module_config {
+ name: "FrameworksTimeCoreTests_android_service",
+ base: "FrameworksTimeCoreTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.service."],
+}
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 4f76dd6..f5b04ee 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -430,6 +430,86 @@
}
@Test
+ public void cropToLength_waveform_underLength() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 1, 2},
+ /* repeatIndex= */ -1);
+ VibrationEffect result = effect.cropToLengthOrNull(5);
+
+ assertThat(result).isEqualTo(effect); // unchanged
+ }
+
+ @Test
+ public void cropToLength_waveform_overLength() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+ /* repeatIndex= */ -1);
+ VibrationEffect result = effect.cropToLengthOrNull(4);
+
+ assertThat(result).isEqualTo(VibrationEffect.createWaveform(
+ new long[]{0, 1, 2, 3},
+ -1));
+ }
+
+ @Test
+ public void cropToLength_waveform_repeating() {
+ // repeating waveforms cannot be truncated
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+ /* repeatIndex= */ 2);
+ VibrationEffect result = effect.cropToLengthOrNull(3);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void cropToLength_waveform_withAmplitudes() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+ /* amplitudes= */ new int[]{10, 20, 40, 10, 20, 40, 10},
+ /* repeatIndex= */ -1);
+ VibrationEffect result = effect.cropToLengthOrNull(3);
+
+ assertThat(result).isEqualTo(VibrationEffect.createWaveform(
+ new long[]{0, 1, 2},
+ new int[]{10, 20, 40},
+ -1));
+ }
+
+ @Test
+ public void cropToLength_composed() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .compose();
+ VibrationEffect result = effect.cropToLengthOrNull(1);
+
+ assertThat(result).isNotNull();
+ assertThat(result).isEqualTo(VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose());
+ }
+
+ @Test
+ public void cropToLength_composed_repeating() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .repeatEffectIndefinitely(TEST_ONE_SHOT)
+ .compose();
+ assertThat(effect.cropToLengthOrNull(1)).isNull();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void cropToLength_vendorEffect() {
+ PersistableBundle vendorData = new PersistableBundle();
+ vendorData.putInt("key", 1);
+ VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData);
+
+ assertThat(effect.cropToLengthOrNull(2)).isNull();
+ }
+
+ @Test
public void getRingtones_noPrebakedRingtones() {
Resources r = mockRingtoneResources(new String[0]);
Context context = mockContext(r);
diff --git a/core/tests/vibrator/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
index 73cd464..c810810 100644
--- a/core/tests/vibrator/src/android/os/VibratorInfoTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
@@ -139,6 +139,35 @@
}
@Test
+ public void testAreEnvelopeEffectsSupported() {
+ VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ assertFalse(noCapabilities.areEnvelopeEffectsSupported());
+ VibratorInfo envelopeEffectCapability = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)
+ .build();
+ assertTrue(envelopeEffectCapability.areEnvelopeEffectsSupported());
+ }
+
+ @Test
+ public void testEnvelopeEffectLimits() {
+ VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setMaxEnvelopeEffectSize(16)
+ .setMinEnvelopeEffectControlPointDurationMillis(20)
+ .setMaxEnvelopeEffectControlPointDurationMillis(1_000)
+ .build();
+ assertEquals(16, info.getMaxEnvelopeEffectSize());
+ assertEquals(20, info.getMinEnvelopeEffectControlPointDurationMillis());
+ assertEquals(1_000, info.getMaxEnvelopeEffectControlPointDurationMillis());
+ assertEquals(16_000, info.getMaxEnvelopeEffectDurationMillis());
+
+ VibratorInfo emptyInfo = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ assertEquals(0, emptyInfo.getMaxEnvelopeEffectSize());
+ assertEquals(0, emptyInfo.getMinEnvelopeEffectControlPointDurationMillis());
+ assertEquals(0, emptyInfo.getMaxEnvelopeEffectControlPointDurationMillis());
+ assertEquals(0, emptyInfo.getMaxEnvelopeEffectDurationMillis());
+ }
+
+ @Test
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
assertEquals(Braking.NONE, new VibratorInfo.Builder(
TEST_VIBRATOR_ID).build().getDefaultBraking());
@@ -262,17 +291,20 @@
VibratorInfo.Builder completeBuilder2 = new VibratorInfo.Builder(TEST_VIBRATOR_ID + 2);
for (VibratorInfo.Builder builder :
- new VibratorInfo.Builder[] {completeBuilder, completeBuilder2}) {
+ new VibratorInfo.Builder[]{completeBuilder, completeBuilder2}) {
builder.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
- .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
- .setPrimitiveDelayMax(100)
- .setCompositionSizeMax(10)
- .setSupportedBraking(Braking.CLAB)
- .setPwlePrimitiveDurationMax(50)
- .setPwleSizeMax(20)
- .setQFactor(2f)
- .setFrequencyProfile(TEST_FREQUENCY_PROFILE);
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setPrimitiveDelayMax(100)
+ .setCompositionSizeMax(10)
+ .setSupportedBraking(Braking.CLAB)
+ .setPwlePrimitiveDurationMax(50)
+ .setPwleSizeMax(20)
+ .setQFactor(2f)
+ .setFrequencyProfile(TEST_FREQUENCY_PROFILE)
+ .setMaxEnvelopeEffectSize(16)
+ .setMinEnvelopeEffectControlPointDurationMillis(20)
+ .setMaxEnvelopeEffectControlPointDurationMillis(1_000);
}
VibratorInfo complete = completeBuilder.build();
diff --git a/core/tests/vibrator/src/android/os/vibrator/VibrationConfigTest.java b/core/tests/vibrator/src/android/os/vibrator/VibrationConfigTest.java
new file mode 100644
index 0000000..a2ff9d7
--- /dev/null
+++ b/core/tests/vibrator/src/android/os/vibrator/VibrationConfigTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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 android.os.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+
+import com.android.internal.R;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class VibrationConfigTest {
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock
+ private Resources mResourcesMock;
+
+ private final Map<String, String> mSystemProperties = new HashMap<>();
+
+ @Test
+ public void getDefaultVibrationAmplitude_returnsConfiguredAmplitude() {
+ when(mResourcesMock.getInteger(R.integer.config_defaultVibrationAmplitude)).thenReturn(1);
+ assertThat(createConfig().getDefaultVibrationAmplitude()).isEqualTo(1);
+
+ when(mResourcesMock.getInteger(R.integer.config_defaultVibrationAmplitude)).thenReturn(123);
+ assertThat(createConfig().getDefaultVibrationAmplitude()).isEqualTo(123);
+
+ when(mResourcesMock.getInteger(R.integer.config_defaultVibrationAmplitude)).thenReturn(255);
+ assertThat(createConfig().getDefaultVibrationAmplitude()).isEqualTo(255);
+ }
+
+ @Test
+ public void getDefaultVibrationAmplitude_invalidValue_returnsMaxAmplitude() {
+ when(mResourcesMock.getInteger(R.integer.config_defaultVibrationAmplitude)).thenReturn(-1);
+ assertThat(createConfig().getDefaultVibrationAmplitude()).isEqualTo(255);
+
+ when(mResourcesMock.getInteger(R.integer.config_defaultVibrationAmplitude)).thenReturn(0);
+ assertThat(createConfig().getDefaultVibrationAmplitude()).isEqualTo(255);
+
+ when(mResourcesMock.getInteger(R.integer.config_defaultVibrationAmplitude)).thenReturn(500);
+ assertThat(createConfig().getDefaultVibrationAmplitude()).isEqualTo(255);
+ }
+
+ @Test
+ public void getDefaultVibrationScaleLevelGain_returnsConfiguredGain() {
+ mSystemProperties.put(VibrationConfig.SCALE_LEVEL_GAIN_SYSTEM_PROPERTY, "1.2");
+ assertThat(createConfig().getDefaultVibrationScaleLevelGain()).isEqualTo(1.2f);
+
+ mSystemProperties.put(VibrationConfig.SCALE_LEVEL_GAIN_SYSTEM_PROPERTY, "2");
+ assertThat(createConfig().getDefaultVibrationScaleLevelGain()).isEqualTo(2f);
+ }
+
+ @Test
+ public void getDefaultVibrationScaleLevelGain_invalidValue_returnsFixedScaleGain() {
+ mSystemProperties.put(VibrationConfig.SCALE_LEVEL_GAIN_SYSTEM_PROPERTY, "");
+ assertThat(createConfig().getDefaultVibrationScaleLevelGain()).isEqualTo(1.4f);
+
+ mSystemProperties.put(VibrationConfig.SCALE_LEVEL_GAIN_SYSTEM_PROPERTY, "invalid");
+ assertThat(createConfig().getDefaultVibrationScaleLevelGain()).isEqualTo(1.4f);
+
+ mSystemProperties.put(VibrationConfig.SCALE_LEVEL_GAIN_SYSTEM_PROPERTY, "-1");
+ assertThat(createConfig().getDefaultVibrationScaleLevelGain()).isEqualTo(1.4f);
+
+ mSystemProperties.put(VibrationConfig.SCALE_LEVEL_GAIN_SYSTEM_PROPERTY, "0.5");
+ assertThat(createConfig().getDefaultVibrationScaleLevelGain()).isEqualTo(1.4f);
+
+ mSystemProperties.put(VibrationConfig.SCALE_LEVEL_GAIN_SYSTEM_PROPERTY, "1.0");
+ assertThat(createConfig().getDefaultVibrationScaleLevelGain()).isEqualTo(1.4f);
+ }
+
+ private VibrationConfig createConfig() {
+ return new VibrationConfig(mResourcesMock, mSystemProperties::get);
+ }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9a55b80..880f30c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -580,6 +580,11 @@
<permission name="android.permission.PREPARE_FACTORY_RESET" />
<!-- Permission required for CTS test - FileIntegrityManagerTest -->
<permission name="android.permission.SETUP_FSVERITY" />
+ <!-- Permissions required for CTS test - AppFunctionManagerTest -->
+ <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" />
+ <permission name="android.permission.EXECUTE_APP_FUNCTIONS" />
+ <!-- Permission required for CTS test - CtsNfcTestCases -->
+ <permission name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/Matrix44.java b/graphics/java/android/graphics/Matrix44.java
index a99e201..683f614 100644
--- a/graphics/java/android/graphics/Matrix44.java
+++ b/graphics/java/android/graphics/Matrix44.java
@@ -19,6 +19,7 @@
import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.graphics.hwui.flags.Flags;
@@ -30,6 +31,7 @@
* in row-major order. The values and operations are treated as column vectors.
*/
@FlaggedApi(Flags.FLAG_MATRIX_44)
+@RavenwoodKeepWholeClass
public class Matrix44 {
final float[] mBackingArray;
/**
diff --git a/graphics/java/android/graphics/OWNERS b/graphics/java/android/graphics/OWNERS
index 9fa8f1b..ef8d26c 100644
--- a/graphics/java/android/graphics/OWNERS
+++ b/graphics/java/android/graphics/OWNERS
@@ -11,3 +11,4 @@
per-file FontFamily.java = file:fonts/OWNERS
per-file FontListParser.java = file:fonts/OWNERS
per-file Typeface.java = file:fonts/OWNERS
+per-file Paint.java = file:fonts/OWNERS
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 618e6dc..c7b8941 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.drawable.Drawable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -35,6 +36,7 @@
* @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider)
* @see Drawable#getOutline(Outline)
*/
+@RavenwoodKeepWholeClass
public final class Outline {
private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY;
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index df95a91..b866382 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1805,16 +1805,7 @@
* @return true if elegant metrics are enabled for text drawing.
*/
public boolean isElegantTextHeight() {
- int rawValue = nGetElegantTextHeight(mNativePaint);
- switch (rawValue) {
- case ELEGANT_TEXT_HEIGHT_DISABLED:
- return false;
- case ELEGANT_TEXT_HEIGHT_ENABLED:
- return true;
- case ELEGANT_TEXT_HEIGHT_UNSET:
- default:
- return com.android.text.flags.Flags.deprecateUiFonts();
- }
+ return nGetElegantTextHeight(mNativePaint) != ELEGANT_TEXT_HEIGHT_DISABLED;
}
// Note: the following three values must be equal to the ones in the JNI file: Paint.cpp
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index 748d66c..76c17154 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
/**
* A {@link Parcelable} wrapper for a {@link ColorSpace}. In order to enable parceling, the
@@ -27,6 +28,7 @@
* {@link ColorSpace.Rgb} instance that has an ICC parametric transfer function as returned by
* {@link ColorSpace.Rgb#getTransferParameters()}.
*/
+@RavenwoodKeepWholeClass
public final class ParcelableColorSpace implements Parcelable {
private final ColorSpace mColorSpace;
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 3ec5b9c..a872e03 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -17,10 +17,12 @@
package android.graphics;
import android.annotation.IntDef;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+@RavenwoodKeepWholeClass
public class PixelFormat {
/** @hide */
@IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE})
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index ba5628c..b7bf055 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -182,10 +182,8 @@
// For ignoring the customization, consume the new-locale-family element but don't
// register any customizations.
- if (com.android.text.flags.Flags.vendorCustomLocaleFallback()) {
- outCustomization.add(new FontConfig.Customization.LocaleFallback(
- Locale.forLanguageTag(lang), intOp, family));
- }
+ outCustomization.add(new FontConfig.Customization.LocaleFallback(
+ Locale.forLanguageTag(lang), intOp, family));
} else {
throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index f727f5b..0e25c34 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -306,13 +306,7 @@
long lastModifiedDate,
int configVersion
) {
- final String fontsXml;
- if (com.android.text.flags.Flags.newFontsFallbackXml()) {
- fontsXml = FONTS_XML;
- } else {
- fontsXml = LEGACY_FONTS_XML;
- }
- return getSystemFontConfigInternal(fontsXml, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
+ return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
updatableFontMap, lastModifiedDate, configVersion);
}
@@ -337,13 +331,7 @@
* @hide
*/
public static @NonNull FontConfig getSystemPreinstalledFontConfig() {
- final String fontsXml;
- if (com.android.text.flags.Flags.newFontsFallbackXml()) {
- fontsXml = FONTS_XML;
- } else {
- fontsXml = LEGACY_FONTS_XML;
- }
- return getSystemFontConfigInternal(fontsXml, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null,
+ return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null,
0, 0);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
index 0726624..bfccb29 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
@@ -16,16 +16,26 @@
package androidx.window.extensions.embedding;
+import static android.window.TaskFragmentOrganizer.KEY_RESTORE_TASK_FRAGMENTS_INFO;
+import static android.window.TaskFragmentOrganizer.KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO;
+
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Looper;
import android.os.MessageQueue;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
+import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentParentInfo;
+import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Helper class to back up and restore the TaskFragmentOrganizer state, in order to resume
@@ -40,11 +50,21 @@
@NonNull
private final SplitController mController;
@NonNull
+ private final SplitPresenter mPresenter;
+ @NonNull
private final BackupIdler mBackupIdler = new BackupIdler();
private boolean mBackupIdlerScheduled;
- BackupHelper(@NonNull SplitController splitController, @NonNull Bundle savedState) {
+ private final List<ParcelableTaskContainerData> mParcelableTaskContainerDataList =
+ new ArrayList<>();
+ private final ArrayMap<IBinder, TaskFragmentInfo> mTaskFragmentInfos = new ArrayMap<>();
+ private final SparseArray<TaskFragmentParentInfo> mTaskFragmentParentInfos =
+ new SparseArray<>();
+
+ BackupHelper(@NonNull SplitController splitController, @NonNull SplitPresenter splitPresenter,
+ @NonNull Bundle savedState) {
mController = splitController;
+ mPresenter = splitPresenter;
if (!savedState.isEmpty()) {
restoreState(savedState);
@@ -58,7 +78,7 @@
void scheduleBackup() {
if (!mBackupIdlerScheduled) {
mBackupIdlerScheduled = true;
- Looper.myQueue().addIdleHandler(mBackupIdler);
+ Looper.getMainLooper().getQueue().addIdleHandler(mBackupIdler);
}
}
@@ -67,13 +87,13 @@
public boolean queueIdle() {
synchronized (mController.mLock) {
mBackupIdlerScheduled = false;
- startBackup();
+ saveState();
}
return false;
}
}
- private void startBackup() {
+ private void saveState() {
final List<TaskContainer> taskContainers = mController.getTaskContainers();
if (taskContainers.isEmpty()) {
Log.w(TAG, "No task-container to back up");
@@ -97,13 +117,92 @@
return;
}
- final List<ParcelableTaskContainerData> parcelableTaskContainerDataList =
- savedState.getParcelableArrayList(KEY_TASK_CONTAINERS,
- ParcelableTaskContainerData.class);
- for (ParcelableTaskContainerData data : parcelableTaskContainerDataList) {
- final TaskContainer taskContainer = new TaskContainer(data, mController);
- if (DEBUG) Log.d(TAG, "Restoring task " + taskContainer.getTaskId());
- // TODO(b/289875940): implement the TaskContainer restoration.
+ if (DEBUG) Log.d(TAG, "Start restoring saved-state");
+ mParcelableTaskContainerDataList.addAll(savedState.getParcelableArrayList(
+ KEY_TASK_CONTAINERS, ParcelableTaskContainerData.class));
+ if (DEBUG) Log.d(TAG, "Retrieved tasks : " + mParcelableTaskContainerDataList.size());
+ if (mParcelableTaskContainerDataList.isEmpty()) {
+ return;
+ }
+
+ final List<TaskFragmentInfo> infos = savedState.getParcelableArrayList(
+ KEY_RESTORE_TASK_FRAGMENTS_INFO, TaskFragmentInfo.class);
+ for (TaskFragmentInfo info : infos) {
+ if (DEBUG) Log.d(TAG, "Retrieved: " + info);
+ mTaskFragmentInfos.put(info.getFragmentToken(), info);
+ mPresenter.updateTaskFragmentInfo(info);
+ }
+
+ final List<TaskFragmentParentInfo> parentInfos = savedState.getParcelableArrayList(
+ KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO,
+ TaskFragmentParentInfo.class);
+ for (TaskFragmentParentInfo info : parentInfos) {
+ if (DEBUG) Log.d(TAG, "Retrieved: " + info);
+ mTaskFragmentParentInfos.put(info.getTaskId(), info);
}
}
-}
+
+ boolean hasPendingStateToRestore() {
+ return !mParcelableTaskContainerDataList.isEmpty();
+ }
+
+ /**
+ * Returns {@code true} if any of the {@link TaskContainer} is restored.
+ * Otherwise, returns {@code false}.
+ */
+ boolean rebuildTaskContainers(@NonNull WindowContainerTransaction wct,
+ @NonNull Set<EmbeddingRule> rules) {
+ if (mParcelableTaskContainerDataList.isEmpty()) {
+ return false;
+ }
+
+ if (DEBUG) Log.d(TAG, "Rebuilding TaskContainers.");
+ final ArrayMap<String, EmbeddingRule> embeddingRuleMap = new ArrayMap<>();
+ for (EmbeddingRule rule : rules) {
+ embeddingRuleMap.put(rule.getTag(), rule);
+ }
+
+ boolean restoredAny = false;
+ for (int i = mParcelableTaskContainerDataList.size() - 1; i >= 0; i--) {
+ final ParcelableTaskContainerData parcelableTaskContainerData =
+ mParcelableTaskContainerDataList.get(i);
+ final List<String> tags = parcelableTaskContainerData.getSplitRuleTags();
+ if (!embeddingRuleMap.containsAll(tags)) {
+ // has unknown tag, unable to restore.
+ if (DEBUG) {
+ Log.d(TAG, "Rebuilding TaskContainer abort! Unknown Tag. Task#"
+ + parcelableTaskContainerData.mTaskId);
+ }
+ continue;
+ }
+
+ mParcelableTaskContainerDataList.remove(parcelableTaskContainerData);
+ final TaskContainer taskContainer = new TaskContainer(parcelableTaskContainerData,
+ mController, mTaskFragmentInfos);
+ if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer);
+ mController.addTaskContainer(taskContainer.getTaskId(), taskContainer);
+
+ for (ParcelableSplitContainerData splitData :
+ parcelableTaskContainerData.getParcelableSplitContainerDataList()) {
+ final SplitRule rule = (SplitRule) embeddingRuleMap.get(splitData.mSplitRuleTag);
+ assert rule != null;
+ if (mController.getContainer(splitData.getPrimaryContainerToken()) != null
+ && mController.getContainer(splitData.getSecondaryContainerToken())
+ != null) {
+ taskContainer.addSplitContainer(
+ new SplitContainer(splitData, mController, rule));
+ }
+ }
+
+ mController.onTaskFragmentParentRestored(wct, taskContainer.getTaskId(),
+ mTaskFragmentParentInfos.get(taskContainer.getTaskId()));
+ restoredAny = true;
+ }
+
+ if (mParcelableTaskContainerDataList.isEmpty()) {
+ mTaskFragmentParentInfos.clear();
+ mTaskFragmentInfos.clear();
+ }
+ return restoredAny;
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 1eb95c1..9ea2943 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -70,6 +70,10 @@
@NonNull
private final TaskFragmentCallback mCallback;
+ @VisibleForTesting
+ @Nullable
+ TaskFragmentAnimationController mAnimationController;
+
/**
* Callback that notifies the controller about changes to task fragments.
*/
@@ -87,6 +91,25 @@
mCallback = callback;
}
+ @Override
+ public void unregisterOrganizer() {
+ if (mAnimationController != null) {
+ mAnimationController.unregisterRemoteAnimations();
+ mAnimationController = null;
+ }
+ super.unregisterOrganizer();
+ }
+
+ /**
+ * Overrides the animation for transitions of embedded activities organized by this organizer.
+ */
+ void overrideSplitAnimation() {
+ if (mAnimationController == null) {
+ mAnimationController = new TaskFragmentAnimationController(this);
+ }
+ mAnimationController.registerRemoteAnimations();
+ }
+
/**
* Starts a new Activity and puts it into split with an existing Activity side-by-side.
* @param launchingFragmentToken token for the launching TaskFragment. If it exists, it will
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
index 817cfce..cb280c5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
@@ -89,13 +89,13 @@
};
@NonNull
- private IBinder getPrimaryContainerToken() {
+ IBinder getPrimaryContainerToken() {
return mSplitContainer != null ? mSplitContainer.getPrimaryContainer().getToken()
: mPrimaryContainerToken;
}
@NonNull
- private IBinder getSecondaryContainerToken() {
+ IBinder getSecondaryContainerToken() {
return mSplitContainer != null ? mSplitContainer.getSecondaryContainer().getToken()
: mSecondaryContainerToken;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java
index 7377d00..97aa699 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java
@@ -108,6 +108,15 @@
: mParcelableSplitContainerDataList;
}
+ @NonNull
+ List<String> getSplitRuleTags() {
+ final List<String> tags = new ArrayList<>();
+ for (ParcelableSplitContainerData data : getParcelableSplitContainerDataList()) {
+ tags.add(data.mSplitRuleTag);
+ }
+ return tags;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 6d436ec..faf73c2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -86,6 +86,25 @@
}
}
+ /** This is only used when restoring it from a {@link ParcelableSplitContainerData}. */
+ SplitContainer(@NonNull ParcelableSplitContainerData parcelableData,
+ @NonNull SplitController splitController, @NonNull SplitRule splitRule) {
+ mParcelableData = parcelableData;
+ mPrimaryContainer = splitController.getContainer(parcelableData.getPrimaryContainerToken());
+ mSecondaryContainer = splitController.getContainer(
+ parcelableData.getSecondaryContainerToken());
+ mSplitRule = splitRule;
+ mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
+ mCurrentSplitAttributes = mDefaultSplitAttributes;
+
+ if (shouldFinishPrimaryWithSecondary(splitRule)) {
+ mSecondaryContainer.addContainerToFinishOnExit(mPrimaryContainer);
+ }
+ if (shouldFinishSecondaryWithPrimary(splitRule)) {
+ mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
+ }
+ }
+
void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) {
if (!mParcelableData.mIsPrimaryContainerMutable) {
throw new IllegalStateException("Cannot update primary TaskFragmentContainer");
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 24b56ae..db4bb0e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -58,18 +58,22 @@
import android.app.ActivityClient;
import android.app.ActivityOptions;
import android.app.ActivityThread;
+import android.app.AppGlobals;
import android.app.Application;
import android.app.Instrumentation;
import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -116,6 +120,7 @@
public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
ActivityEmbeddingComponent, DividerPresenter.DragEventCallback {
static final String TAG = "SplitController";
+ static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
// TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
// association. It's not set in WM Extensions nor Wm Jetpack library currently.
@@ -274,6 +279,26 @@
Log.i(TAG, "Setting embedding rules. Size: " + rules.size());
mSplitRules.clear();
mSplitRules.addAll(rules);
+
+ if (!Flags.aeBackStackRestore() || !mPresenter.isRebuildTaskContainersNeeded()) {
+ return;
+ }
+
+ try {
+ final TransactionRecord transactionRecord =
+ mTransactionManager.startNewTransaction();
+ final WindowContainerTransaction wct = transactionRecord.getTransaction();
+ if (mPresenter.rebuildTaskContainers(wct, rules)) {
+ transactionRecord.apply(false /* shouldApplyIndependently */);
+ updateCallbackIfNecessary();
+ } else {
+ transactionRecord.abort();
+ }
+ } catch (IllegalStateException ex) {
+ Log.e(TAG, "Having an existing transaction while running restoration with"
+ + "new rules!! It is likely too late to perform the restoration "
+ + "already!?", ex);
+ }
}
}
@@ -898,6 +923,12 @@
}
@GuardedBy("mLock")
+ void onTaskFragmentParentRestored(@NonNull WindowContainerTransaction wct, int taskId,
+ @NonNull TaskFragmentParentInfo parentInfo) {
+ onTaskFragmentParentInfoChanged(wct, taskId, parentInfo);
+ }
+
+ @GuardedBy("mLock")
void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) {
final TaskContainer taskContainer = getTaskContainer(taskId);
if (taskContainer == null) {
@@ -919,7 +950,8 @@
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
- final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
+ final List<TaskFragmentContainer> containers
+ = new ArrayList<>(taskContainer.getTaskFragmentContainers());
for (int i = containers.size() - 1; i >= 0; i--) {
final TaskFragmentContainer container = containers.get(i);
// Wait until onTaskFragmentAppeared to update new container.
@@ -3307,4 +3339,17 @@
transactionRecord.apply(false /* shouldApplyIndependently */);
}
}
+
+ // TODO(b/207070762): cleanup with legacy app transition
+ private static boolean getShellTransitEnabled() {
+ try {
+ if (AppGlobals.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ return SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Error getting system features");
+ }
+ return true;
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 5637320..0c0ded9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -24,6 +24,7 @@
import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
import android.annotation.AnimRes;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.WindowConfiguration;
@@ -47,7 +48,6 @@
import android.window.WindowContainerTransaction;
import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.window.extensions.core.util.function.Function;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
@@ -67,6 +67,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -174,13 +175,27 @@
} else {
registerOrganizer();
}
- mBackupHelper = new BackupHelper(controller, outSavedState);
+ mBackupHelper = new BackupHelper(controller, this, outSavedState);
+ if (!SplitController.ENABLE_SHELL_TRANSITIONS) {
+ // TODO(b/207070762): cleanup with legacy app transition
+ // Animation will be handled by WM Shell when Shell transition is enabled.
+ overrideSplitAnimation();
+ }
}
void scheduleBackup() {
mBackupHelper.scheduleBackup();
}
+ boolean isRebuildTaskContainersNeeded() {
+ return mBackupHelper.hasPendingStateToRestore();
+ }
+
+ boolean rebuildTaskContainers(@NonNull WindowContainerTransaction wct,
+ @NonNull Set<EmbeddingRule> rules) {
+ return mBackupHelper.rebuildTaskContainers(wct, rules);
+ }
+
/**
* Deletes the specified container and all other associated and dependent containers in the same
* transaction.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 82dfda5..74cce68 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -31,6 +31,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -136,6 +137,7 @@
mInfo = new TaskFragmentParentInfo(
taskProperties.getConfiguration(),
taskProperties.getDisplayId(),
+ taskId,
// Note that it is always called when there's a new Activity is started, which
// implies the host task is visible and has an activity in the task.
true /* visible */,
@@ -146,14 +148,23 @@
/** This is only used when restoring it from a {@link ParcelableTaskContainerData}. */
TaskContainer(@NonNull ParcelableTaskContainerData data,
- @NonNull SplitController splitController) {
+ @NonNull SplitController splitController,
+ @NonNull ArrayMap<IBinder, TaskFragmentInfo> taskFragmentInfoMap) {
mParcelableTaskContainerData = new ParcelableTaskContainerData(data, this);
+ mInfo = new TaskFragmentParentInfo(new Configuration(), 0 /* displayId */, -1 /* taskId */,
+ false /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
mSplitController = splitController;
for (ParcelableTaskFragmentContainerData tfData :
data.getParcelableTaskFragmentContainerDataList()) {
- final TaskFragmentContainer container =
- new TaskFragmentContainer(tfData, splitController, this);
- mContainers.add(container);
+ final TaskFragmentInfo info = taskFragmentInfoMap.get(tfData.mToken);
+ if (info != null && !info.isEmpty()) {
+ final TaskFragmentContainer container =
+ new TaskFragmentContainer(tfData, splitController, this);
+ container.setInfo(new WindowContainerTransaction(), info);
+ mContainers.add(container);
+ } else {
+ Log.d(TAG, "Drop " + tfData + " while restoring Task " + data.mTaskId);
+ }
}
}
@@ -194,7 +205,8 @@
void setInvisible() {
mInfo = new TaskFragmentParentInfo(mInfo.getConfiguration(), mInfo.getDisplayId(),
- false /* visible */, mInfo.hasDirectActivity(), mInfo.getDecorSurface());
+ mInfo.getTaskId(), false /* visible */, mInfo.hasDirectActivity(),
+ mInfo.getDecorSurface());
}
boolean hasDirectActivity() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
new file mode 100644
index 0000000..33220c4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static android.graphics.Matrix.MTRANS_X;
+import static android.graphics.Matrix.MTRANS_Y;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Choreographer;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
+ *
+ * The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close.
+ */
+class TaskFragmentAnimationAdapter {
+
+ /**
+ * If {@link #mOverrideLayer} is set to this value, we don't want to override the surface layer.
+ */
+ private static final int LAYER_NO_OVERRIDE = -1;
+
+ @NonNull
+ final Animation mAnimation;
+ @NonNull
+ final RemoteAnimationTarget mTarget;
+ @NonNull
+ final SurfaceControl mLeash;
+ /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+ @NonNull
+ private final Rect mWholeAnimationBounds = new Rect();
+ /**
+ * Area in absolute coordinate that should represent all the content to show for this window.
+ * This should be the end bounds for opening window, and start bounds for closing window in case
+ * the window is resizing during the open/close transition.
+ */
+ @NonNull
+ private final Rect mContentBounds = new Rect();
+ /** Offset relative to the window parent surface for {@link #mContentBounds}. */
+ @NonNull
+ private final Point mContentRelOffset = new Point();
+
+ @NonNull
+ final Transformation mTransformation = new Transformation();
+ @NonNull
+ final float[] mMatrix = new float[9];
+ @NonNull
+ final float[] mVecs = new float[4];
+ @NonNull
+ final Rect mRect = new Rect();
+ private boolean mIsFirstFrame = true;
+ private int mOverrideLayer = LAYER_NO_OVERRIDE;
+
+ TaskFragmentAnimationAdapter(@NonNull Animation animation,
+ @NonNull RemoteAnimationTarget target) {
+ this(animation, target, target.leash, target.screenSpaceBounds);
+ }
+
+ /**
+ * @param leash the surface to animate.
+ * @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't
+ * go beyond.
+ */
+ TaskFragmentAnimationAdapter(@NonNull Animation animation,
+ @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
+ @NonNull Rect wholeAnimationBounds) {
+ mAnimation = animation;
+ mTarget = target;
+ mLeash = leash;
+ mWholeAnimationBounds.set(wholeAnimationBounds);
+ if (target.mode == MODE_CLOSING) {
+ // When it is closing, we want to show the content at the start position in case the
+ // window is resizing as well. For example, when the activities is changing from split
+ // to stack, the bottom TaskFragment will be resized to fullscreen when hiding.
+ final Rect startBounds = target.startBounds;
+ final Rect endBounds = target.screenSpaceBounds;
+ mContentBounds.set(startBounds);
+ mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
+ mContentRelOffset.offset(
+ startBounds.left - endBounds.left,
+ startBounds.top - endBounds.top);
+ } else {
+ mContentBounds.set(target.screenSpaceBounds);
+ mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
+ }
+ }
+
+ /**
+ * Surface layer to be set at the first frame of the animation. We will not set the layer if it
+ * is set to {@link #LAYER_NO_OVERRIDE}.
+ */
+ final void overrideLayer(int layer) {
+ mOverrideLayer = layer;
+ }
+
+ /** Called on frame update. */
+ final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
+ if (mIsFirstFrame) {
+ t.show(mLeash);
+ if (mOverrideLayer != LAYER_NO_OVERRIDE) {
+ t.setLayer(mLeash, mOverrideLayer);
+ }
+ mIsFirstFrame = false;
+ }
+
+ // Extract the transformation to the current time.
+ mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
+ mTransformation);
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ onAnimationUpdateInner(t);
+ }
+
+ /** To be overridden by subclasses to adjust the animation surface change. */
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Update the surface position and alpha.
+ mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+
+ // Get current surface bounds in absolute coordinate.
+ // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
+ final int positionX = Math.round(mMatrix[MTRANS_X]);
+ final int positionY = Math.round(mMatrix[MTRANS_Y]);
+ final Rect cropRect = new Rect(mContentBounds);
+ cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y);
+
+ // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+ final int offsetX = cropRect.left;
+ final int offsetY = cropRect.top;
+
+ // Intersect to make sure the animation happens within the whole animation bounds.
+ if (!cropRect.intersect(mWholeAnimationBounds)) {
+ // Hide the surface when it is outside of the animation area.
+ t.setAlpha(mLeash, 0);
+ }
+
+ // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+ cropRect.offset(-offsetX, -offsetY);
+ t.setCrop(mLeash, cropRect);
+ }
+
+ /** Called after animation finished. */
+ final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
+ onAnimationUpdate(t, mAnimation.getDuration());
+ }
+
+ final long getDurationHint() {
+ return mAnimation.computeDurationHint();
+ }
+
+ /**
+ * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
+ * size change.
+ */
+ static class SnapshotAdapter extends TaskFragmentAnimationAdapter {
+
+ SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
+ // Start leash is the snapshot of the starting surface.
+ super(animation, target, target.startLeash, target.screenSpaceBounds);
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Snapshot should always be placed at the top left of the animation leash.
+ mTransformation.getMatrix().postTranslate(0, 0);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+ }
+ }
+
+ /**
+ * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
+ */
+ static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
+
+ BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
+ super(animation, target);
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ mTransformation.getMatrix().postTranslate(
+ mTarget.localBounds.left, mTarget.localBounds.top);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+
+ // The following applies an inverse scale to the clip-rect so that it crops "after" the
+ // scale instead of before.
+ mVecs[1] = mVecs[2] = 0;
+ mVecs[0] = mVecs[3] = 1;
+ mTransformation.getMatrix().mapVectors(mVecs);
+ mVecs[0] = 1.f / mVecs[0];
+ mVecs[3] = 1.f / mVecs[3];
+ final Rect clipRect = mTransformation.getClipRect();
+ mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f);
+ mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f);
+ mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f);
+ mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f);
+ t.setWindowCrop(mLeash, mRect);
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
new file mode 100644
index 0000000..d7eb9a0
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+
+import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.window.TaskFragmentOrganizer;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Controls the TaskFragment remote animations. */
+class TaskFragmentAnimationController {
+
+ private static final String TAG = "TaskFragAnimationCtrl";
+ static final boolean DEBUG = false;
+
+ private final TaskFragmentOrganizer mOrganizer;
+ private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner();
+ @VisibleForTesting
+ final RemoteAnimationDefinition mDefinition;
+ private boolean mIsRegistered;
+
+ TaskFragmentAnimationController(@NonNull TaskFragmentOrganizer organizer) {
+ mOrganizer = organizer;
+ mDefinition = new RemoteAnimationDefinition();
+ final RemoteAnimationAdapter animationAdapter =
+ new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
+ }
+
+ void registerRemoteAnimations() {
+ if (DEBUG) {
+ Log.v(TAG, "registerRemoteAnimations");
+ }
+ if (mIsRegistered) {
+ return;
+ }
+ mOrganizer.registerRemoteAnimations(mDefinition);
+ mIsRegistered = true;
+ }
+
+ void unregisterRemoteAnimations() {
+ if (DEBUG) {
+ Log.v(TAG, "unregisterRemoteAnimations");
+ }
+ if (!mIsRegistered) {
+ return;
+ }
+ mOrganizer.unregisterRemoteAnimations();
+ mIsRegistered = false;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
new file mode 100644
index 0000000..d9b73a8
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_CHANGING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+
+/** To run the TaskFragment animations. */
+class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
+
+ private static final String TAG = "TaskFragAnimationRunner";
+ private final Handler mHandler;
+ private final TaskFragmentAnimationSpec mAnimationSpec;
+
+ TaskFragmentAnimationRunner() {
+ HandlerThread animationThread = new HandlerThread(
+ "androidx.window.extensions.embedding", THREAD_PRIORITY_DISPLAY);
+ animationThread.start();
+ mHandler = animationThread.getThreadHandler();
+ mAnimationSpec = new TaskFragmentAnimationSpec(mHandler);
+ }
+
+ @Nullable
+ private Animator mAnimator;
+
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ @NonNull RemoteAnimationTarget[] apps,
+ @NonNull RemoteAnimationTarget[] wallpapers,
+ @NonNull RemoteAnimationTarget[] nonApps,
+ @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+ if (wallpapers.length != 0 || nonApps.length != 0) {
+ throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
+ + "wallpaper or non-app windows.");
+ }
+ if (TaskFragmentAnimationController.DEBUG) {
+ Log.v(TAG, "onAnimationStart transit=" + transit);
+ }
+ mHandler.post(() -> startAnimation(transit, apps, finishedCallback));
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ mHandler.post(this::cancelAnimation);
+ }
+
+ /** Creates and starts animation. */
+ private void startAnimation(@WindowManager.TransitionOldType int transit,
+ @NonNull RemoteAnimationTarget[] targets,
+ @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+ if (mAnimator != null) {
+ Log.w(TAG, "start new animation when the previous one is not finished yet.");
+ mAnimator.cancel();
+ }
+ mAnimator = createAnimator(transit, targets, finishedCallback);
+ mAnimator.start();
+ }
+
+ /** Cancels animation. */
+ private void cancelAnimation() {
+ if (mAnimator == null) {
+ return;
+ }
+ mAnimator.cancel();
+ mAnimator = null;
+ }
+
+ /** Creates the animator given the transition type and windows. */
+ @NonNull
+ private Animator createAnimator(@WindowManager.TransitionOldType int transit,
+ @NonNull RemoteAnimationTarget[] targets,
+ @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+ final List<TaskFragmentAnimationAdapter> adapters =
+ createAnimationAdapters(transit, targets);
+ long duration = 0;
+ for (TaskFragmentAnimationAdapter adapter : adapters) {
+ duration = Math.max(duration, adapter.getDurationHint());
+ }
+ final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.setDuration(duration);
+ animator.addUpdateListener((anim) -> {
+ // Update all adapters in the same transaction.
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (TaskFragmentAnimationAdapter adapter : adapters) {
+ adapter.onAnimationUpdate(t, animator.getCurrentPlayTime());
+ }
+ t.apply();
+ });
+ animator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (TaskFragmentAnimationAdapter adapter : adapters) {
+ adapter.onAnimationEnd(t);
+ }
+ t.apply();
+
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ mAnimator = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+ });
+ return animator;
+ }
+
+ /** List of {@link TaskFragmentAnimationAdapter} to handle animations on all window targets. */
+ @NonNull
+ private List<TaskFragmentAnimationAdapter> createAnimationAdapters(
+ @WindowManager.TransitionOldType int transit,
+ @NonNull RemoteAnimationTarget[] targets) {
+ switch (transit) {
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
+ return createOpenAnimationAdapters(targets);
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
+ return createCloseAnimationAdapters(targets);
+ case TRANSIT_OLD_TASK_FRAGMENT_CHANGE:
+ return createChangeAnimationAdapters(targets);
+ default:
+ throw new IllegalArgumentException("Unhandled transit type=" + transit);
+ }
+ }
+
+ @NonNull
+ private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets) {
+ return createOpenCloseAnimationAdapters(targets, true /* isOpening */,
+ mAnimationSpec::loadOpenAnimation);
+ }
+
+ @NonNull
+ private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets) {
+ return createOpenCloseAnimationAdapters(targets, false /* isOpening */,
+ mAnimationSpec::loadCloseAnimation);
+ }
+
+ /**
+ * Creates {@link TaskFragmentAnimationAdapter} for OPEN and CLOSE types of transition.
+ * @param isOpening {@code true} for OPEN type, {@code false} for CLOSE type.
+ */
+ @NonNull
+ private List<TaskFragmentAnimationAdapter> createOpenCloseAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets, boolean isOpening,
+ @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider) {
+ // We need to know if the target window is only a partial of the whole animation screen.
+ // If so, we will need to adjust it to make the whole animation screen looks like one.
+ final List<RemoteAnimationTarget> openingTargets = new ArrayList<>();
+ final List<RemoteAnimationTarget> closingTargets = new ArrayList<>();
+ final Rect openingWholeScreenBounds = new Rect();
+ final Rect closingWholeScreenBounds = new Rect();
+ for (RemoteAnimationTarget target : targets) {
+ if (target.mode != MODE_CLOSING) {
+ openingTargets.add(target);
+ openingWholeScreenBounds.union(target.screenSpaceBounds);
+ } else {
+ closingTargets.add(target);
+ closingWholeScreenBounds.union(target.screenSpaceBounds);
+ // Union the start bounds since this may be the ClosingChanging animation.
+ closingWholeScreenBounds.union(target.startBounds);
+ }
+ }
+
+ // For OPEN transition, open windows should be above close windows.
+ // For CLOSE transition, open windows should be below close windows.
+ int offsetLayer = TYPE_LAYER_OFFSET;
+ final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+ for (RemoteAnimationTarget target : openingTargets) {
+ final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target,
+ animationProvider, openingWholeScreenBounds);
+ if (isOpening) {
+ adapter.overrideLayer(offsetLayer++);
+ }
+ adapters.add(adapter);
+ }
+ for (RemoteAnimationTarget target : closingTargets) {
+ final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target,
+ animationProvider, closingWholeScreenBounds);
+ if (!isOpening) {
+ adapter.overrideLayer(offsetLayer++);
+ }
+ adapters.add(adapter);
+ }
+ return adapters;
+ }
+
+ @NonNull
+ private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter(
+ @NonNull RemoteAnimationTarget target,
+ @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
+ @NonNull Rect wholeAnimationBounds) {
+ final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
+ return new TaskFragmentAnimationAdapter(animation, target, target.leash,
+ wholeAnimationBounds);
+ }
+
+ @NonNull
+ private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets) {
+ if (shouldUseJumpCutForChangeAnimation(targets)) {
+ return new ArrayList<>();
+ }
+
+ final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+ for (RemoteAnimationTarget target : targets) {
+ if (target.mode == MODE_CHANGING) {
+ // This is the target with bounds change.
+ final Animation[] animations =
+ mAnimationSpec.createChangeBoundsChangeAnimations(target);
+ // Adapter for the starting snapshot leash.
+ adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter(
+ animations[0], target));
+ // Adapter for the ending bounds changed leash.
+ adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter(
+ animations[1], target));
+ continue;
+ }
+
+ // These are the other targets that don't have bounds change in the same transition.
+ final Animation animation;
+ if (target.hasAnimatingParent) {
+ // No-op if it will be covered by the changing parent window.
+ animation = TaskFragmentAnimationSpec.createNoopAnimation(target);
+ } else if (target.mode == MODE_CLOSING) {
+ animation = mAnimationSpec.createChangeBoundsCloseAnimation(target);
+ } else {
+ animation = mAnimationSpec.createChangeBoundsOpenAnimation(target);
+ }
+ adapters.add(new TaskFragmentAnimationAdapter(animation, target));
+ }
+ return adapters;
+ }
+
+ /**
+ * Whether we should use jump cut for the change transition.
+ * This normally happens when opening a new secondary with the existing primary using a
+ * different split layout. This can be complicated, like from horizontal to vertical split with
+ * new split pairs.
+ * Uses a jump cut animation to simplify.
+ */
+ private boolean shouldUseJumpCutForChangeAnimation(@NonNull RemoteAnimationTarget[] targets) {
+ boolean hasOpeningWindow = false;
+ boolean hasClosingWindow = false;
+ for (RemoteAnimationTarget target : targets) {
+ if (target.hasAnimatingParent) {
+ continue;
+ }
+ hasOpeningWindow |= target.mode == MODE_OPENING;
+ hasClosingWindow |= target.mode == MODE_CLOSING;
+ }
+ return hasOpeningWindow && hasClosingWindow;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
new file mode 100644
index 0000000..1f866c3
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.provider.Settings;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.TransitionAnimation;
+
+/** Animation spec for TaskFragment transition. */
+// TODO(b/206557124): provide an easier way to customize animation
+class TaskFragmentAnimationSpec {
+
+ private static final String TAG = "TaskFragAnimationSpec";
+ private static final int CHANGE_ANIMATION_DURATION = 517;
+ private static final int CHANGE_ANIMATION_FADE_DURATION = 80;
+ private static final int CHANGE_ANIMATION_FADE_OFFSET = 30;
+
+ private final Context mContext;
+ private final TransitionAnimation mTransitionAnimation;
+ private final Interpolator mFastOutExtraSlowInInterpolator;
+ private final LinearInterpolator mLinearInterpolator;
+ private float mTransitionAnimationScaleSetting;
+
+ TaskFragmentAnimationSpec(@NonNull Handler handler) {
+ mContext = ActivityThread.currentActivityThread().getApplication();
+ mTransitionAnimation = new TransitionAnimation(mContext, false /* debug */, TAG);
+ // Initialize the AttributeCache for the TransitionAnimation.
+ AttributeCache.init(mContext);
+ mFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.fast_out_extra_slow_in);
+ mLinearInterpolator = new LinearInterpolator();
+
+ // The transition animation should be adjusted based on the developer option.
+ final ContentResolver resolver = mContext.getContentResolver();
+ mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
+ new SettingsObserver(handler));
+ }
+
+ /** For target that doesn't need to be animated. */
+ @NonNull
+ static Animation createNoopAnimation(@NonNull RemoteAnimationTarget target) {
+ // Noop but just keep the target showing/hiding.
+ final float alpha = target.mode == MODE_CLOSING ? 0f : 1f;
+ return new AlphaAnimation(alpha, alpha);
+ }
+
+ /** Animation for target that is opening in a change transition. */
+ @NonNull
+ Animation createChangeBoundsOpenAnimation(@NonNull RemoteAnimationTarget target) {
+ final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+ final Rect bounds = target.screenSpaceBounds;
+ final int startLeft;
+ final int startTop;
+ if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+ // The window will be animated in from left or right depending on its position.
+ startTop = 0;
+ startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
+ } else {
+ // The window will be animated in from top or bottom depending on its position.
+ startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
+ startLeft = 0;
+ }
+
+ // The position should be 0-based as we will post translate in
+ // TaskFragmentAnimationAdapter#onAnimationUpdate
+ final Animation animation = new TranslateAnimation(startLeft, 0, startTop, 0);
+ animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+ animation.setDuration(CHANGE_ANIMATION_DURATION);
+ animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ /** Animation for target that is closing in a change transition. */
+ @NonNull
+ Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
+ final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+ // Use startBounds if the window is closing in case it may also resize.
+ final Rect bounds = target.startBounds;
+ final int endTop;
+ final int endLeft;
+ if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+ // The window will be animated out to left or right depending on its position.
+ endTop = 0;
+ endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
+ } else {
+ // The window will be animated out to top or bottom depending on its position.
+ endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
+ endLeft = 0;
+ }
+
+ // The position should be 0-based as we will post translate in
+ // TaskFragmentAnimationAdapter#onAnimationUpdate
+ final Animation animation = new TranslateAnimation(0, endLeft, 0, endTop);
+ animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+ animation.setDuration(CHANGE_ANIMATION_DURATION);
+ animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ /**
+ * Animation for target that is changing (bounds change) in a change transition.
+ * @return the return array always has two elements. The first one is for the start leash, and
+ * the second one is for the end leash.
+ */
+ @NonNull
+ Animation[] createChangeBoundsChangeAnimations(@NonNull RemoteAnimationTarget target) {
+ // Both start bounds and end bounds are in screen coordinates. We will post translate
+ // to the local coordinates in TaskFragmentAnimationAdapter#onAnimationUpdate
+ final Rect startBounds = target.startBounds;
+ final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+ final Rect endBounds = target.screenSpaceBounds;
+ float scaleX = ((float) startBounds.width()) / endBounds.width();
+ float scaleY = ((float) startBounds.height()) / endBounds.height();
+ // Start leash is a child of the end leash. Reverse the scale so that the start leash won't
+ // be scaled up with its parent.
+ float startScaleX = 1.f / scaleX;
+ float startScaleY = 1.f / scaleY;
+
+ // The start leash will be fade out.
+ final AnimationSet startSet = new AnimationSet(false /* shareInterpolator */);
+ final Animation startAlpha = new AlphaAnimation(1f, 0f);
+ startAlpha.setInterpolator(mLinearInterpolator);
+ startAlpha.setDuration(CHANGE_ANIMATION_FADE_DURATION);
+ startAlpha.setStartOffset(CHANGE_ANIMATION_FADE_OFFSET);
+ startSet.addAnimation(startAlpha);
+ final Animation startScale = new ScaleAnimation(startScaleX, startScaleX, startScaleY,
+ startScaleY);
+ startScale.setInterpolator(mFastOutExtraSlowInInterpolator);
+ startScale.setDuration(CHANGE_ANIMATION_DURATION);
+ startSet.addAnimation(startScale);
+ startSet.initialize(startBounds.width(), startBounds.height(), endBounds.width(),
+ endBounds.height());
+ startSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+ // The end leash will be moved into the end position while scaling.
+ final AnimationSet endSet = new AnimationSet(true /* shareInterpolator */);
+ endSet.setInterpolator(mFastOutExtraSlowInInterpolator);
+ final Animation endScale = new ScaleAnimation(scaleX, 1, scaleY, 1);
+ endScale.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(endScale);
+ // The position should be 0-based as we will post translate in
+ // TaskFragmentAnimationAdapter#onAnimationUpdate
+ final Animation endTranslate = new TranslateAnimation(startBounds.left - endBounds.left, 0,
+ startBounds.top - endBounds.top, 0);
+ endTranslate.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(endTranslate);
+ // The end leash is resizing, we should update the window crop based on the clip rect.
+ final Rect startClip = new Rect(startBounds);
+ final Rect endClip = new Rect(endBounds);
+ startClip.offsetTo(0, 0);
+ endClip.offsetTo(0, 0);
+ final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
+ clipAnim.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(clipAnim);
+ endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(),
+ parentBounds.height());
+ endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+ return new Animation[]{startSet, endSet};
+ }
+
+ @NonNull
+ Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
+ @NonNull Rect wholeAnimationBounds) {
+ final boolean isEnter = target.mode != MODE_CLOSING;
+ final Animation animation;
+ // Background color on TaskDisplayArea has already been set earlier in
+ // WindowContainer#getAnimationAdapter.
+ if (target.showBackdrop) {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
+ : com.android.internal.R.anim.task_fragment_clear_top_open_exit);
+ } else {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_open_enter
+ : com.android.internal.R.anim.task_fragment_open_exit);
+ }
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are opening at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are launching together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
+ wholeAnimationBounds.width(), wholeAnimationBounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ @NonNull
+ Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
+ @NonNull Rect wholeAnimationBounds) {
+ final boolean isEnter = target.mode != MODE_CLOSING;
+ final Animation animation;
+ if (target.showBackdrop) {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
+ : com.android.internal.R.anim.task_fragment_clear_top_close_exit);
+ } else {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_close_enter
+ : com.android.internal.R.anim.task_fragment_close_exit);
+ }
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are closing at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are finishing together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
+ wholeAnimationBounds.width(), wholeAnimationBounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ private float getTransitionAnimationScaleSetting() {
+ return WindowManager.fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat(
+ R.dimen.config_appTransitionAnimationDurationScaleDefault)));
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ SettingsObserver(@NonNull Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 8911d18..ac004c3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -23,6 +23,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -84,6 +86,24 @@
}
@Test
+ public void testUnregisterOrganizer() {
+ mOrganizer.overrideSplitAnimation();
+ mOrganizer.unregisterOrganizer();
+
+ verify(mOrganizer).unregisterRemoteAnimations();
+ }
+
+ @Test
+ public void testOverrideSplitAnimation() {
+ assertNull(mOrganizer.mAnimationController);
+
+ mOrganizer.overrideSplitAnimation();
+
+ assertNotNull(mOrganizer.mAnimationController);
+ verify(mOrganizer).registerRemoteAnimations(mOrganizer.mAnimationController.mDefinition);
+ }
+
+ @Test
public void testExpandTaskFragment() {
final TaskContainer taskContainer = createTestTaskContainer();
doReturn(taskContainer).when(mSplitController).getTaskContainer(anyInt());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 90eeb58..5b97e7e 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -549,7 +549,7 @@
assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
@@ -618,7 +618,8 @@
final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
- true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+ TASK_ID, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */);
parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10);
mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
@@ -642,7 +643,8 @@
final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
- true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+ TASK_ID, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */);
mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index d852204..0512412 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1164,7 +1164,7 @@
public void testOnTransactionReady_taskFragmentParentInfoChanged() {
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */);
transaction.addChange(new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
@@ -1625,7 +1625,7 @@
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
final Configuration configuration = new Configuration();
final TaskFragmentParentInfo originalInfo = new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */);
mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
TASK_ID, originalInfo);
@@ -1634,7 +1634,7 @@
// Making a public configuration change while the Task is invisible.
configuration.densityDpi += 100;
final TaskFragmentParentInfo invisibleInfo = new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, false /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, false /* visible */, false /* hasDirectActivity */,
null /* decorSurface */);
mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
TASK_ID, invisibleInfo);
@@ -1646,7 +1646,7 @@
// Updates when Task to become visible
final TaskFragmentParentInfo visibleInfo = new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */);
mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
TASK_ID, visibleInfo);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 2847232..97f4d07 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -23,6 +23,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static org.junit.Assert.assertEquals;
@@ -82,7 +83,7 @@
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
@@ -90,7 +91,7 @@
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
assertEquals(WINDOWING_MODE_FREEFORM,
@@ -111,14 +112,14 @@
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
assertFalse(taskContainer.isInPictureInPicture());
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
assertTrue(taskContainer.isInPictureInPicture());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
new file mode 100644
index 0000000..a1e9f08
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 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 androidx.window.extensions.embedding;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.mockito.Mockito.never;
+
+import android.platform.test.annotations.Presubmit;
+import android.window.TaskFragmentOrganizer;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Test class for {@link TaskFragmentAnimationController}.
+ *
+ * Build/Install/Run:
+ * atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TaskFragmentAnimationControllerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock
+ private TaskFragmentOrganizer mOrganizer;
+ private TaskFragmentAnimationController mAnimationController;
+
+ @Before
+ public void setup() {
+ mAnimationController = new TaskFragmentAnimationController(mOrganizer);
+ }
+
+ @Test
+ public void testRegisterRemoteAnimations() {
+ mAnimationController.registerRemoteAnimations();
+
+ verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
+
+ mAnimationController.registerRemoteAnimations();
+
+ // No extra call if it has been registered.
+ verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
+ }
+
+ @Test
+ public void testUnregisterRemoteAnimations() {
+ mAnimationController.unregisterRemoteAnimations();
+
+ // No call if it is not registered.
+ verify(mOrganizer, never()).unregisterRemoteAnimations();
+
+ mAnimationController.registerRemoteAnimations();
+ mAnimationController.unregisterRemoteAnimations();
+
+ verify(mOrganizer).unregisterRemoteAnimations();
+
+ mAnimationController.unregisterRemoteAnimations();
+
+ // No extra call if it has been unregistered.
+ verify(mOrganizer).unregisterRemoteAnimations();
+ }
+}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index b338a2a..a79bc97 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -39,17 +39,6 @@
path: "src",
}
-// Sources that have no dependencies that can be used directly downstream of this library
-// TODO(b/322791067): move these sources to WindowManager-Shell-shared
-filegroup {
- name: "wm_shell_util-sources",
- srcs: [
- "src/com/android/wm/shell/common/bubbles/*.kt",
- "src/com/android/wm/shell/common/bubbles/*.java",
- ],
- path: "src",
-}
-
// Aidls which can be used directly downstream of this library
filegroup {
name: "wm_shell-aidls",
@@ -184,9 +173,11 @@
":wm_shell-shared-aidls",
],
static_libs: [
+ "androidx.core_core-animation",
"androidx.dynamicanimation_dynamicanimation",
"jsr330",
],
+ kotlincflags: ["-Xjvm-default=all"],
}
java_library {
@@ -212,7 +203,6 @@
],
static_libs: [
"androidx.appcompat_appcompat",
- "androidx.core_core-animation",
"androidx.core_core-ktx",
"androidx.arch.core_core-runtime",
"androidx.datastore_datastore",
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index bbbc23e..3b739c3 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -23,6 +23,7 @@
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.WAKEUP_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+ <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
<application>
<activity
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 9de10c0..526ccd5 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -25,11 +25,10 @@
}
flag {
- name: "enable_pip2_implementation"
+ name: "enable_pip2"
namespace: "multitasking"
description: "Enables the new implementation of PiP (PiP2)"
- bug: "290220798"
- is_fixed_read_only: true
+ bug: "311462191"
}
flag {
@@ -138,3 +137,17 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_bubble_to_fullscreen"
+ namespace: "multitasking"
+ description: "Enable an option to move bubbles to fullscreen"
+ bug: "363326492"
+}
+
+flag {
+ name: "enable_flexible_split"
+ namespace: "multitasking"
+ description: "Enables flexibile split feature for split screen"
+ bug: "349828130"
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
index d35f493..f09969d 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.bubbles
import android.view.LayoutInflater
-import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.shared.bubbles.BubblePopupView
import com.android.wm.shell.testing.goldenpathmanager.WMShellGoldenPathManager
import com.android.wm.shell.R
import org.junit.Rule
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index 4b97451..b38d00da 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -30,7 +30,7 @@
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index faadf1d..96ffa03 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -53,7 +53,7 @@
import org.mockito.kotlin.mock
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
new file mode 100644
index 0000000..35d459f
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -0,0 +1,324 @@
+/*
+ * 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.bubbles.bar
+
+import android.app.ActivityManager
+import android.content.Context
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.R
+import com.android.wm.shell.bubbles.Bubble
+import com.android.wm.shell.bubbles.BubbleData
+import com.android.wm.shell.bubbles.BubbleExpandedViewManager
+import com.android.wm.shell.bubbles.BubblePositioner
+import com.android.wm.shell.bubbles.BubbleTaskView
+import com.android.wm.shell.bubbles.BubbleTaskViewFactory
+import com.android.wm.shell.bubbles.DeviceConfig
+import com.android.wm.shell.bubbles.RegionSamplingProvider
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.handles.RegionSamplingHelper
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import java.util.Collections
+import java.util.concurrent.Executor
+
+/** Tests for [BubbleBarExpandedViewTest] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleBarExpandedViewTest {
+ companion object {
+ const val SCREEN_WIDTH = 2000
+ const val SCREEN_HEIGHT = 1000
+ }
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private val windowManager = context.getSystemService(WindowManager::class.java)
+
+ private lateinit var mainExecutor: TestExecutor
+ private lateinit var bgExecutor: TestExecutor
+
+ private lateinit var expandedViewManager: BubbleExpandedViewManager
+ private lateinit var positioner: BubblePositioner
+ private lateinit var bubbleTaskView: BubbleTaskView
+
+ private lateinit var bubbleExpandedView: BubbleBarExpandedView
+ private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null
+ private var regionSamplingProvider: TestRegionSamplingProvider? = null
+
+ @Before
+ fun setUp() {
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ mainExecutor = TestExecutor()
+ bgExecutor = TestExecutor()
+ positioner = BubblePositioner(context, windowManager)
+ positioner.setShowingInBubbleBar(true)
+ val deviceConfig =
+ DeviceConfig(
+ windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+ isLargeScreen = true,
+ isSmallTablet = false,
+ isLandscape = true,
+ isRtl = false,
+ insets = Insets.of(10, 20, 30, 40)
+ )
+ positioner.update(deviceConfig)
+
+ expandedViewManager = createExpandedViewManager()
+ bubbleTaskView = FakeBubbleTaskViewFactory().create()
+
+ val inflater = LayoutInflater.from(context)
+
+ regionSamplingProvider = TestRegionSamplingProvider()
+
+ bubbleExpandedView = (inflater.inflate(
+ R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */
+ ) as BubbleBarExpandedView)
+ bubbleExpandedView.initialize(
+ expandedViewManager,
+ positioner,
+ false /* isOverflow */,
+ bubbleTaskView,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingProvider
+ )
+
+ getInstrumentation().runOnMainSync(Runnable {
+ bubbleExpandedView.onAttachedToWindow()
+ // Helper should be created once attached to window
+ testableRegionSamplingHelper = regionSamplingProvider!!.helper
+ })
+ }
+
+ @After
+ fun tearDown() {
+ testableRegionSamplingHelper?.stopAndDestroy()
+ }
+
+ @Test
+ fun testCreateSamplingHelper_onAttach() {
+ assertThat(testableRegionSamplingHelper).isNotNull()
+ }
+
+ @Test
+ fun testDestroySamplingHelper_onDetach() {
+ bubbleExpandedView.onDetachedFromWindow()
+ assertThat(testableRegionSamplingHelper!!.isDestroyed).isTrue()
+ }
+
+ @Test
+ fun testStopSampling_onDragStart() {
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+
+ bubbleExpandedView.setDragging(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ @Test
+ fun testStartSampling_onDragEnd() {
+ bubbleExpandedView.setDragging(true)
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+
+ bubbleExpandedView.setDragging(false)
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testStartSampling_onContentVisible() {
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.setWindowVisible).isTrue()
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testStopSampling_onContentInvisible() {
+ bubbleExpandedView.setContentVisibility(false)
+
+ assertThat(testableRegionSamplingHelper!!.setWindowInvisible).isTrue()
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ @Test
+ fun testSampling_startStopAnimating_visible() {
+ bubbleExpandedView.isAnimating = true
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+
+ bubbleExpandedView.isAnimating = false
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testSampling_startStopAnimating_invisible() {
+ bubbleExpandedView.isAnimating = true
+ bubbleExpandedView.setContentVisibility(false)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ testableRegionSamplingHelper!!.reset()
+
+ bubbleExpandedView.isAnimating = false
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
+ override fun create(): BubbleTaskView {
+ val taskViewTaskController = mock<TaskViewTaskController>()
+ val taskView = TaskView(context, taskViewTaskController)
+ val taskInfo = mock<ActivityManager.RunningTaskInfo>()
+ whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+ return BubbleTaskView(taskView, mainExecutor)
+ }
+ }
+
+ private inner class TestRegionSamplingProvider : RegionSamplingProvider {
+
+ lateinit var helper: TestableRegionSamplingHelper
+
+ override fun createHelper(
+ sampledView: View?,
+ callback: RegionSamplingHelper.SamplingCallback?,
+ backgroundExecutor: Executor?,
+ mainExecutor: Executor?
+ ): RegionSamplingHelper {
+ helper = TestableRegionSamplingHelper(sampledView, callback, backgroundExecutor,
+ mainExecutor)
+ return helper
+ }
+ }
+
+ private inner class TestableRegionSamplingHelper(
+ sampledView: View?,
+ samplingCallback: SamplingCallback?,
+ backgroundExecutor: Executor?,
+ mainExecutor: Executor?
+ ) : RegionSamplingHelper(sampledView, samplingCallback, backgroundExecutor, mainExecutor) {
+
+ var isStarted = false
+ var isStopped = false
+ var isDestroyed = false
+ var setWindowVisible = false
+ var setWindowInvisible = false
+
+ override fun start(initialSamplingBounds: Rect) {
+ super.start(initialSamplingBounds)
+ isStarted = true
+ }
+
+ override fun stop() {
+ super.stop()
+ isStopped = true
+ }
+
+ override fun stopAndDestroy() {
+ super.stopAndDestroy()
+ isDestroyed = true
+ }
+
+ override fun setWindowVisible(visible: Boolean) {
+ super.setWindowVisible(visible)
+ if (visible) {
+ setWindowVisible = true
+ } else {
+ setWindowInvisible = true
+ }
+ }
+
+ fun reset() {
+ isStarted = false
+ isStopped = false
+ isDestroyed = false
+ setWindowVisible = false
+ setWindowInvisible = false
+ }
+ }
+
+ private fun createExpandedViewManager(): BubbleExpandedViewManager {
+ return object : BubbleExpandedViewManager {
+ override val overflowBubbles: List<Bubble>
+ get() = Collections.emptyList()
+
+ override fun setOverflowListener(listener: BubbleData.Listener) {
+ }
+
+ override fun collapseStack() {
+ }
+
+ override fun updateWindowFlagsForBackpress(intercept: Boolean) {
+ }
+
+ override fun promoteBubbleFromOverflow(bubble: Bubble) {
+ }
+
+ override fun removeBubble(key: String, reason: Int) {
+ }
+
+ override fun dismissBubble(bubble: Bubble, reason: Int) {
+ }
+
+ override fun setAppBubbleTaskId(key: String, taskId: Int) {
+ }
+
+ override fun isStackExpanded(): Boolean {
+ return true
+ }
+
+ override fun isShowingAsBubbleBar(): Boolean {
+ return true
+ }
+
+ override fun hideCurrentInputMethod() {
+ }
+
+ override fun updateBubbleBarLocation(location: BubbleBarLocation) {
+ }
+ }
+ }
+
+ private class TestExecutor : ShellExecutor {
+
+ private val runnables: MutableList<Runnable> = mutableListOf()
+
+ override fun execute(runnable: Runnable) {
+ runnables.add(runnable)
+ }
+
+ override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
+ execute(runnable)
+ }
+
+ override fun removeCallbacks(runnable: Runnable?) {}
+
+ override fun hasCallback(runnable: Runnable?): Boolean = false
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index 935d129..ecb2b25 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -31,12 +31,12 @@
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.DeviceConfig
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index a0a06f1..806d026 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.wm.shell.common.bubbles.BubblePopupView
+<com.android.wm.shell.shared.bubbles.BubblePopupView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -53,4 +53,4 @@
android:textAlignment="center"
android:text="@string/bubble_bar_education_manage_text"/>
-</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file
+</com.android.wm.shell.shared.bubbles.BubblePopupView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index b489a5c..7fa586c 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.wm.shell.common.bubbles.BubblePopupView
+<com.android.wm.shell.shared.bubbles.BubblePopupView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -53,4 +53,4 @@
android:textAlignment="center"
android:text="@string/bubble_bar_education_stack_text"/>
-</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file
+</com.android.wm.shell.shared.bubbles.BubblePopupView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index d1b98a6..0ed5a72 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> in"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-instellings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Maak borrel toe"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Moenie dat gesprek \'n borrel word nie"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Klets met borrels"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nuwe gesprekke verskyn as swerwende ikone, of borrels Tik op borrel om dit oop te maak. Sleep om dit te skuif."</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 8044719..c4d9158 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ሰብስብ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"የ<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ቅንብሮች"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"አረፋን አሰናብት"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ውይይቶችን በአረፋ አታሳይ"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"አረፋዎችን በመጠቀም ይወያዩ"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"አዲስ ውይይቶች እንደ ተንሳፋፊ አዶዎች ወይም አረፋዎች ሆነው ይታያሉ። አረፋን ለመክፈት መታ ያድርጉ። ለመውሰድ ይጎትቱት።"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 21aa34e..ced9ee9 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"تصغير <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"إعدادات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"إغلاق فقاعة المحادثة"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"عدم عرض المحادثة كفقاعة محادثة"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"الدردشة باستخدام فقاعات المحادثات"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"تظهر المحادثات الجديدة كرموز عائمة أو كفقاعات. انقر لفتح فقاعة المحادثة، واسحبها لتحريكها."</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index c59f470..273d043 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> সংকোচন কৰক"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিং"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল অগ্ৰাহ্য কৰক"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"বাৰ্তালাপ বাবল নকৰিব"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"নতুন বাৰ্তালাপ উপঙি থকা চিহ্নসমূহ অথবা bubbles হিচাপে প্ৰদর্শিত হয়। Bubbles খুলিবলৈ টিপক। এইটো স্থানান্তৰ কৰিবলৈ টানি নিয়ক।"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 841323e..81bb544 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"yığcamlaşdırın: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Yumrucuğu ləğv edin"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Söhbəti yumrucuqda göstərmə"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Yumrucuqlardan istifadə edərək söhbət edin"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Yeni söhbətlər üzən nişanlar və ya yumrucuqlar kimi görünür. Yumrucuğu açmaq üçün toxunun. Hərəkət etdirmək üçün sürüşdürün."</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 86ab548..898b844 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skupite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Podešavanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblačiće za konverzaciju"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Ćaskajte u oblačićima"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nove konverzacije se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da biste otvorili oblačić. Prevucite da biste ga premestili."</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index bcbc1ae..c340729 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: згарнуць"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Налады \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Адхіліць апавяшчэнне"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Усплывальныя чаты"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Новыя размовы будуць паказвацца як рухомыя значкі ці ўсплывальныя чаты. Націсніце, каб адкрыць усплывальны чат. Перацягніце яго, каб перамясціць."</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 4d1208b..076a815 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"свиване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Настройки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Отхвърляне на балончетата"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Без балончета за разговора"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Чат с балончета"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Новите разговори се показват като плаващи икони, или балончета. Докоснете балонче, за да го отворите, или го плъзнете, за да го преместите."</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index bf8bc99..b0d6696 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> আড়াল করুন"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> সেটিংস"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল খারিজ করুন"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"কথোপকথন বাবল হিসেবে দেখাবে না"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"বাবল ব্যবহার করে চ্যাট করুন"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"নতুন কথোপকথন ভেসে থাকা আইকন বা বাবল হিসেবে দেখানো হয়। বাবল খুলতে ট্যাপ করুন। সেটি সরাতে ধরে টেনে আনুন।"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index cf53d25..0196e5e 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sužavanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke aplikacije <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nemoj prikazivati razgovor u oblačićima"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatajte koristeći oblačiće"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi razgovori se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da otvorite oblačić. Prevucite da ga premjestite."</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 87ea62e..fa4b627 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"replega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuració de l\'aplicació <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora la bombolla"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostris la conversa com a bombolla"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Xateja amb bombolles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Les converses noves es mostren com a icones flotants o bombolles. Toca per obrir una bombolla. Arrossega-la per moure-la."</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index e21213b..3956fca 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavřít bublinu"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovat konverzaci v bublinách"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatujte pomocí bublin"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nové konverzace se zobrazují jako plovoucí ikony, neboli bubliny. Klepnutím bublinu otevřete. Přetažením ji posunete."</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 1c4647f..afe4a1ab 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Indstillinger for <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Afvis boble"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Vis ikke samtaler i bobler"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat ved hjælp af bobler"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som svævende ikoner eller bobler. Tryk for at åbne boblen. Træk for at flytte den."</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 88a5789..1e50339 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> minimieren"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Einstellungen für <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles zum Chatten verwenden"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, „Bubbles“ genannt. Wenn du eine Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index beeefee..5c3c6de 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"σύμπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Ρυθμίσεις <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Παράβλ. για συννεφ."</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Να μην γίνει προβολή της συζήτησης σε συννεφάκια."</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Συζητήστε χρησιμοποιώντας συννεφάκια."</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή συννεφάκια. Πατήστε για να ανοίξετε το συννεφάκι. Σύρετε για να το μετακινήσετε."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 72f4070..51c69e5 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index d11f521..f5b0a27 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -73,6 +73,7 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubble_fullscreen_text" msgid="1006758103218086231">"Move to fullscreen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 72f4070..51c69e5 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 72f4070..51c69e5 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 8002468a..6292be5 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -73,6 +73,7 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubble_fullscreen_text" msgid="1006758103218086231">"Move to fullscreen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 5756aae..8644780 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat con burbujas"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 3c55bf6..9718bf1 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Ajustes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatea con burbujas"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamados \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index d921967..d36a8d1 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ahenda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Rakenduse <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> seaded"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Sule mull"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ära kuva vestlust mullina"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Vestelge mullide abil"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Uued vestlused kuvatakse hõljuvate ikoonidena ehk mullidena. Puudutage mulli avamiseks. Lohistage mulli, et seda liigutada."</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index f319af1..2ee086e 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tolestu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikazioaren ezarpenak"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Baztertu burbuila"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ez erakutsi elkarrizketak burbuila gisa"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Txateatu burbuilen bidez"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Elkarrizketa berriak ikono gainerakor edo burbuila gisa agertzen dira. Sakatu burbuila irekitzeko. Arrasta ezazu mugitzeko."</string>
@@ -80,7 +82,7 @@
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Aplikazioaren burbuilak desaktibatzeko, sakatu Kudeatu"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ados"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ez dago azkenaldiko burbuilarik"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azken burbuilak eta baztertutakoak agertuko dira hemen"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azkenaldiko burbuilak eta baztertutakoak agertuko dira hemen"</string>
<string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Txateatu burbuilak erabilita"</string>
<string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Elkarrizketa berriak ikono gisa agertzen dira pantailaren beheko izkinan. Zabaltzeko, saka itzazu. Baztertzeko, aldiz, arrasta itzazu."</string>
<string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolatu burbuilak edonoiz"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 44a0929..f4cdd5f 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"جمع کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"تنظیمات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"رد کردن حبابک"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"مکالمه در حباب نشان داده نشود"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابکها"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمههای جدید بهصورت نمادهای شناور یا حبابکها نشان داده میشوند. برای باز کردن حبابکها تکضرب بزنید. برای جابهجایی، آن را بکشید."</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 59cd6e0..6be2ee2 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tiivistä <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: asetukset"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ohita kupla"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Älä näytä kuplia keskusteluista"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chattaile kuplien avulla"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Uudet keskustelut näkyvät kelluvina kuvakkeina tai kuplina. Avaa kupla napauttamalla. Siirrä sitä vetämällä."</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 02f832b..5470099 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorer la bulle"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher les conversations dans des bulles"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Clavarder en utilisant des bulles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes (de bulles). Touchez une bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 5d916f4..63b5994 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Fermer la bulle"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatter en utilisant des bulles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou de bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index e1b2a7e..36ad521 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar burbulla"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mostrar a conversa como burbulla"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatear usando burbullas"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"As conversas novas aparecen como iconas flotantes ou burbullas. Toca para abrir a burbulla e arrastra para movela."</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index fecce73..868ef5b 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> નાનું કરો"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> સેટિંગ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"બબલને છોડી દો"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"વાતચીતને બબલ કરશો નહીં"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"બબલનો ઉપયોગ કરીને ચૅટ કરો"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"નવી વાતચીત ફ્લોટિંગ આઇકન અથવા બબલ જેવી દેખાશે. બબલને ખોલવા માટે ટૅપ કરો. તેને ખસેડવા માટે ખેંચો."</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index f889f20..31c7307 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को छोटा करें"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> की सेटिंग"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारिज करें"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"बातचीत को बबल न करें"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"बबल्स का इस्तेमाल करके चैट करें"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"नई बातचीत फ़्लोटिंग आइकॉन या बबल्स की तरह दिखेंगी. बबल को खोलने के लिए टैप करें. इसे एक जगह से दूसरी जगह ले जाने के लिए खींचें और छोड़ें."</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 04053c8..d99a65d8 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sažmite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Zaustavi razgovor u oblačićima"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Oblačići u chatu"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi razgovori pojavljuju se kao pomične ikone ili oblačići. Dodirnite za otvaranje oblačića. Povucite da biste ga premjestili."</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index bb52649..bed760e 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> összecsukása"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> beállításai"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Buborék elvetése"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne jelenjen meg a beszélgetés buborékban"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Buborékokat használó csevegés"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Az új beszélgetések lebegő ikonként, vagyis buborékként jelennek meg. A buborék megnyitásához koppintson rá. Áthelyezéshez húzza a kívánt helyre."</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index fff5a10..fcb7254 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծալել"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – կարգավորումներ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Փակել ամպիկը"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Զրույցը չցուցադրել ամպիկի տեսքով"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Զրույցի ամպիկներ"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Նոր զրույցները կհայտնվեն լողացող պատկերակների կամ ամպիկների տեսքով։ Հպեք՝ ամպիկը բացելու համար։ Քաշեք՝ այն տեղափոխելու համար։"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index a957754..85a9bbf 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ciutkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Setelan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Tutup balon"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan gunakan percakapan balon"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat dalam tampilan balon"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Percakapan baru muncul sebagai ikon mengambang, atau balon. Ketuk untuk membuka balon. Tarik untuk memindahkannya."</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 7b91768..8041162 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"minnka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Stillingar <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Loka blöðru"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ekki setja samtal í blöðru"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Spjalla með blöðrum"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Ný samtöl birtast sem fljótandi tákn eða blöðrur. Ýttu til að opna blöðru. Dragðu hana til að færa."</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 4ae4b36..3ba6873 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"comprimi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Impostazioni <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta utilizzando le bolle"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index ea73653..e1854fa1 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"כיווץ של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"הגדרות <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"סגירת בועה"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"אין להציג בועות לשיחה"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"לדבר בבועות"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"שיחות חדשות מופיעות כסמלים צפים, או בועות. יש להקיש כדי לפתוח בועה. יש לגרור כדי להזיז אותה."</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 0cb921c..1f1ddc7 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を閉じます"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> の設定"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"バブルを閉じる"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 16e99ba..e201a20 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის ჩაკეცვა"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-ის პარამეტრები"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ბუშტის დახურვა"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"აიკრძალოს საუბრის ბუშტები"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"ჩეთი ბუშტების გამოყენებით"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"ახალი საუბრები გამოჩნდება როგორც მოტივტივე ხატულები ან ბუშტები. შეეხეთ ბუშტის გასახსნელად. გადაიტანეთ ჩავლებით."</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index c6f558f..1c335dd 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жию"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлері"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Қалқымалы хабарды жабу"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Әңгіменің қалқыма хабары көрсетілмесін"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Қалқыма хабарлар арқылы сөйлесу"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңа әңгімелер қалқыма белгішелер немесе хабарлар түрінде көрсетіледі. Қалқыма хабарды ашу үшін түртіңіз. Жылжыту үшін сүйреңіз."</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 508ea48..d0cceee 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"បង្រួម <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"ការកំណត់ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ច្រានចោលពពុះ"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"កុំបង្ហាញការសន្ទនាជាពពុះ"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"ជជែកដោយប្រើពពុះ"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"ការសន្ទនាថ្មីៗបង្ហាញជាពពុះ ឬរូបអណ្ដែត។ ចុច ដើម្បីបើកពពុះ។ អូស ដើម្បីផ្លាស់ទីពពុះនេះ។"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 1fc627b..63b5c68 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ಬಬಲ್ ವಜಾಗೊಳಿಸಿ"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ಸಂಭಾಷಣೆಯನ್ನು ಬಬಲ್ ಮಾಡಬೇಡಿ"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"ಬಬಲ್ಸ್ ಬಳಸಿ ಚಾಟ್ ಮಾಡಿ"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"ಹೊಸ ಸಂಭಾಷಣೆಗಳು ತೇಲುವ ಐಕಾನ್ಗಳು ಅಥವಾ ಬಬಲ್ಸ್ ಆಗಿ ಗೋಚರಿಸುತ್ತವೆ. ಬಬಲ್ ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಅದನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಲು ಎಳೆಯಿರಿ."</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 96d360e..b5efd10 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 접기"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> 설정"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"대화창 닫기"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"대화를 대화창으로 표시하지 않기"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"대화창으로 채팅하기"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"새로운 대화가 플로팅 아이콘인 대화창으로 표시됩니다. 대화창을 열려면 탭하세요. 드래그하여 이동할 수 있습니다."</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 662c2eae..e001efe 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жыйыштыруу"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлери"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Калкып чыкма билдирмени жабуу"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн тийип коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index ed6b378..029c95b 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ຫຍໍ້ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ລົງ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"ການຕັ້ງຄ່າ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ປິດຟອງໄວ້"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ຢ່າໃຊ້ຟອງໃນການສົນທະນາ"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"ສົນທະນາໂດຍໃຊ້ຟອງ"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"ການສົນທະນາໃໝ່ຈະປາກົດເປັນໄອຄອນ ຫຼື ຟອງແບບລອຍ. ແຕະເພື່ອເປີດຟອງ. ລາກເພື່ອຍ້າຍມັນ."</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index f71d650..791ddcd 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sutraukti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ nustatymai"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Atsisakyti burbulo"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerodyti pokalbio burbule"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Pokalbis naudojant burbulus"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nauji pokalbiai rodomi kaip slankiosios piktogramos arba burbulai. Palieskite, kad atidarytumėte burbulą. Vilkite, kad perkeltumėte."</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index abadef7..8a86687 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Sakļaut “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Lietotnes <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iestatījumi"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Nerādīt burbuli"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerādīt sarunu burbuļos"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Tērzēšana, izmantojot burbuļus"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Jaunas sarunas tiek rādītas kā peldošas ikonas vai burbuļi. Pieskarieties, lai atvērtu burbuli. Velciet, lai to pārvietotu."</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 0576fc0..3a6b2f0 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"собери <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Поставки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Отфрли балонче"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не прикажувај го разговорот во балончиња"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Разговор во балончиња"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Новите разговори ќе се појавуваат како лебдечки икони или балончиња. Допрете за отворање на балончето. Повлечете за да го преместите."</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 6e7ea08..26e4a46 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ചുരുക്കുക"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ക്രമീകരണം"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ബബിൾ ഡിസ്മിസ് ചെയ്യൂ"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"സംഭാഷണം ബബിൾ ചെയ്യരുത്"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"ബബിളുകൾ ഉപയോഗിച്ച് ചാറ്റ് ചെയ്യുക"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ഇത് നീക്കാൻ വലിച്ചിടുക."</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index d69ec05..505a4ad 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г хураах"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-н тохиргоо"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Бөмбөлгийг хаах"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Харилцан яриаг бүү бөмбөлөг болго"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Бөмбөлөг ашиглан чатлаарай"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Шинэ харилцан яриа нь хөвөгч дүрс тэмдэг эсвэл бөмбөлөг хэлбэрээр харагддаг. Бөмбөлгийг нээхийн тулд товшино уу. Түүнийг зөөхийн тулд чирнэ үү."</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 33ba1c2..cc35f11 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोलॅप्स करा"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> सेटिंग्ज"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल डिसमिस करा"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"संभाषणाला बबल करू नका"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"बबल वापरून चॅट करा"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"नवीन संभाषणे फ्लोटिंग आयकन किंवा बबल म्हणून दिसतात. बबल उघडण्यासाठी टॅप करा. हे हलवण्यासाठी ड्रॅग करा."</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index e024e4b..61d6614 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kuncupkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Tetapan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ketepikan gelembung"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan jadikan perbualan dalam bentuk gelembung"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bersembang menggunakan gelembung"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Perbualan baharu muncul sebagai ikon terapung atau gelembung. Ketik untuk membuka gelembung. Seret untuk mengalihkan gelembung tersebut."</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index bd680b4..7841e07 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချုံ့ရန်"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ဆက်တင်များ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ပူဖောင်းကွက် ပယ်ရန်"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"စကားဝိုင်းကို ပူဖောင်းကွက် မပြုလုပ်ပါနှင့်"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"ပူဖောင်းကွက် သုံး၍ ချတ်လုပ်ခြင်း"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"စကားဝိုင်းအသစ်များကို မျောနေသည့် သင်္ကေတများ သို့မဟုတ် ပူဖောင်းကွက်များအဖြစ် မြင်ရပါမည်။ ပူဖောင်းကွက်ကိုဖွင့်ရန် တို့ပါ။ ရွှေ့ရန် ၎င်းကို ဖိဆွဲပါ။"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 896d9fd..ea2ae1b 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-innstillinger"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Lukk boblen"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ikke vis samtaler i bobler"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat med bobler"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som flytende ikoner eller bobler. Trykk for å åpne en boble. Dra for å flytte den."</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 113085e..a3bd5ee 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> का सेटिङहरू"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारेज गर्नुहोस्"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"वार्तालाप बबलको रूपमा नदेखाउनुहोस्"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"बबलहरू प्रयोग गरी कुराकानी गर्नुहोस्"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"नयाँ वार्तालापहरू तैरने आइकन वा बबलका रूपमा देखिन्छन्। बबल खोल्न ट्याप गर्नुहोस्। बबल सार्न सो बबललाई ड्र्याग गर्नुहोस्।"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index a9c06fb..ad2c60f 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> samenvouwen"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden als zwevende iconen of bubbels getoond. Tik om een bubbel te openen. Sleep om een bubbel te verplaatsen."</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index a80cfc2..76f2715 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ସେଟିଂସ୍"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ବବଲ୍ ଖାରଜ କରନ୍ତୁ"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ବାର୍ତ୍ତାଳାପକୁ ବବଲ୍ କରନ୍ତୁ ନାହିଁ"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"ବବଲଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଚାଟ୍ କରନ୍ତୁ"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"ନୂଆ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଫ୍ଲୋଟିଂ ଆଇକନ୍ କିମ୍ବା ବବଲ୍ ଭାବେ ଦେଖାଯିବ। ବବଲ୍ ଖୋଲିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଏହାକୁ ମୁଭ୍ କରିବାକୁ ଟାଣନ୍ତୁ।"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 7257161..cd7fd47 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ਸੈਟਿੰਗਾਂ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ਗੱਲਬਾਤ \'ਤੇ ਬਬਲ ਨਾ ਲਾਓ"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"ਬਬਲ ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"ਨਵੀਆਂ ਗੱਲਾਂਬਾਤਾਂ ਫਲੋਟਿੰਗ ਪ੍ਰਤੀਕਾਂ ਜਾਂ ਬਬਲ ਦੇ ਰੂਪ ਵਿੱਚ ਦਿਸਦੀਆਂ ਹਨ। ਬਬਲ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਇਸਨੂੰ ਲਿਜਾਣ ਲਈ ਘਸੀਟੋ।"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 7600db0..d33b6f1 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – ustawienia"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Zamknij dymek"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nie wyświetlaj rozmowy jako dymka"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Czatuj, korzystając z dymków"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 58c78f3..c7a00ff 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse usando balões"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index f433413..d141447 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"reduzir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Definições de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar balão"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não apresentar a conversa em balões"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse no chat através de balões"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"As novas conversas aparecem como ícones flutuantes ou balões. Toque para abrir o balão. Arraste para o mover."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 58c78f3..c7a00ff 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse usando balões"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 077503a..9bc7660 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"restrânge <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Închide balonul"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișa conversația în balon"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat cu baloane"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Conversațiile noi apar ca pictograme flotante sau baloane. Atinge pentru a deschide balonul. Trage pentru a-l muta."</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 5471027..044e3b02 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Свернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: настройки"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Скрыть всплывающий чат"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показывать всплывающий чат для разговора"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Всплывающие чаты"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Новые разговоры будут появляться в виде плавающих значков, или всплывающих чатов. Чтобы открыть чат, нажмите на него, а чтобы переместить – перетащите."</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 3f015f6..da2541e 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> හකුළන්න"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සැකසීම්"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"බුබුලු ඉවත ලන්න"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"සංවාදය බුබුලු නොදමන්න"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"බුබුලු භාවිතයෙන් කතාබහ කරන්න"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"නව සංවාද පාවෙන අයිකන හෝ බුබුලු ලෙස දිස් වේ. බුබුල විවෘත කිරීමට තට්ටු කරන්න. එය ගෙන යාමට අදින්න."</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index fa376e7..394a4ca 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavenia aplikácie <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavrieť bublinu"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovať konverzáciu ako bublinu"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Čet pomocou bublín"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nové konverzácie sa zobrazujú ako plávajúce ikony či bubliny. Bublinu otvoríte klepnutím. Premiestnite ju presunutím."</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 8538668..a90c2c2 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"strnitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavitve za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Opusti oblaček"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Pogovora ne prikaži v oblačku"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Klepet z oblački"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi pogovori so prikazani kot lebdeče ikone ali oblački. Če želite odpreti oblaček, se ga dotaknite. Če ga želite premakniti, ga povlecite."</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index f77a43d..706e75f 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"palos <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Cilësimet e <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Hiqe flluskën"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Mos e vendos bisedën në flluskë"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bisedo duke përdorur flluskat"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Bisedat e reja shfaqen si ikona pluskuese ose flluska. Trokit për të hapur flluskën. Zvarrit për ta zhvendosur."</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index af7686a..539a00a2 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"скупите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Подешавања за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Одбаци облачић"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не користи облачиће за конверзацију"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Ћаскајте у облачићима"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Нове конверзације се приказују као плутајуће иконе или облачићи. Додирните да бисте отворили облачић. Превуците да бисте га преместили."</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 0d08d8d..549eb10 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"komprimera <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Inställningar för <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Stäng bubbla"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Visa inte konversationen i bubblor"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta med bubblor"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nya konversationer visas som flytande ikoner, så kallade bubblor. Tryck på bubblan om du vill öppna den. Dra den om du vill flytta den."</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 448f6249..9c34690 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kunja <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Mipangilio ya <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ondoa kiputo"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Usiweke viputo kwenye mazungumzo"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Piga gumzo ukitumia viputo"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Mazungumzo mapya huonekena kama aikoni au viputo vinavyoelea. Gusa ili ufungue kiputo. Buruta ili ukisogeze."</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 1268929..7e996f3 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐச் சுருக்கும்"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> அமைப்புகள்"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"குமிழை அகற்று"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"உரையாடலைக் குமிழாக்காதே"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"குமிழ்களைப் பயன்படுத்தி அரட்டையடியுங்கள்"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"புதிய உரையாடல்கள் மிதக்கும் ஐகான்களாகவோ குமிழ்களாகவோ தோன்றும். குமிழைத் திறக்க தட்டவும். நகர்த்த இழுக்கவும்."</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 524e558..984cea8 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ను కుదించండి"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> సెట్టింగ్లు"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"బబుల్ను విస్మరించు"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"సంభాషణను బబుల్ చేయవద్దు"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"బబుల్స్ను ఉపయోగించి చాట్ చేయండి"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"కొత్త సంభాషణలు తేలియాడే చిహ్నాలుగా లేదా బబుల్స్ లాగా కనిపిస్తాయి. బబుల్ని తెరవడానికి నొక్కండి. తరలించడానికి లాగండి."</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 00a395f..3460b07 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ยุบ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"การตั้งค่า <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ปิดบับเบิล"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ไม่ต้องแสดงการสนทนาเป็นบับเบิล"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"แชทโดยใช้บับเบิล"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"การสนทนาใหม่ๆ จะปรากฏเป็นไอคอนแบบลอยหรือบับเบิล แตะเพื่อเปิดบับเบิล ลากเพื่อย้ายที่"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 50a9211..dc30912 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"i-collapse ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Mga setting ng <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"I-dismiss ang bubble"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Huwag ipakita sa bubble ang mga pag-uusap"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Mag-chat gamit ang bubbles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Lumalabas bilang mga nakalutang na icon o bubble ang mga bagong pag-uusap. I-tap para buksan ang bubble. I-drag para ilipat ito."</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index ddd4206..e9e2173 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"daralt: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Baloncuğu kapat"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Görüşmeyi baloncuk olarak görüntüleme"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Baloncukları kullanarak sohbet edin"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Yeni görüşmeler kayan simgeler veya baloncuk olarak görünür. Açmak için baloncuğa dokunun. Baloncuğu taşımak için sürükleyin."</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 1dcdfe6..e1b6e35 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"згорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Налаштування параметра \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Закрити підказку"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показувати спливаючі чати для розмов"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Спливаючий чат"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Нові повідомлення чату з\'являються у вигляді спливаючих значків. Щоб відкрити чат, натисніть його, а щоб перемістити – перетягніть."</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 26ece5c..0508e6d 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو سکیڑیں"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ترتیبات"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"بلبلہ برخاست کریں"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"گفتگو بلبلہ نہ کریں"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"بلبلے کے ذریعے چیٹ کریں"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"نئی گفتگوئیں فلوٹنگ آئیکن یا بلبلے کے طور پر ظاہر ہوں گی۔ بلبلہ کھولنے کے لیے تھپتھپائیں۔ اسے منتقل کرنے کے لیے گھسیٹیں۔"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 90b9a3f..5de3b7b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yopish"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> sozlamalari"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bulutchani yopish"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Suhbatlar bulutchalar shaklida chiqmasin"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bulutchalar yordamida subhatlashish"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Yangi xabarlar qalqib chiquvchi belgilar yoki bulutchalar kabi chiqadi. Xabarni ochish uchun bildirishnoma ustiga bosing. Xabarni qayta joylash uchun bildirishnomani suring."</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 90471f9..9b2f898 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"thu gọn <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Cài đặt <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Đóng bong bóng"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Dừng sử dụng bong bóng cho cuộc trò chuyện"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Trò chuyện bằng bong bóng trò chuyện"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng nổi hoặc bong bóng trò chuyện. Nhấn để mở bong bóng trò chuyện. Kéo để di chuyển bong bóng trò chuyện."</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 0aa52ac..b45b76e 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收起“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>设置"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"关闭消息气泡"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不以消息气泡形式显示对话"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"使用消息气泡聊天"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"新对话会以浮动图标或消息气泡形式显示。点按即可打开消息气泡。拖动即可移动消息气泡。"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 8a5be6a..ae776b8 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收埋<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉小視窗氣泡"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要透過小視窗顯示對話"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"使用小視窗進行即時通訊"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"新對話會以浮動圖示 (小視窗) 顯示。輕按即可開啟小視窗。拖曳即可移動小視窗。"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index d1cc4bb..5bfc6b8 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收合「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉對話框"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要以對話框形式顯示對話"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"透過對話框來聊天"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"新的對話會以浮動圖示或對話框形式顯示。輕觸即可開啟對話框,拖曳則可移動對話框。"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 6163a97..0598d62 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -73,6 +73,8 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"goqa <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> izilungiselelo"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Cashisa ibhamuza"</string>
+ <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+ <skip />
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ungayibhamuzi ingxoxo"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Xoxa usebenzisa amabhamuza"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Izingxoxo ezintsha zivela njengezithonjana ezintantayo, noma amabhamuza. Thepha ukuze uvule ibhamuza. Hudula ukuze ulihambise."</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 2d98a2b..755e0d5 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -569,7 +569,7 @@
<!-- The thickness in dp for all desktop drag transition regions. -->
<dimen name="desktop_mode_transition_region_thickness">44dp</dimen>
- <item type="dimen" format="float" name="desktop_mode_fullscreen_region_scale">0.4</item>
+ <item type="dimen" format="float" name="desktop_mode_fullscreen_region_scale">0.2</item>
<!-- The height on the screen where drag to the left or right edge will result in a
desktop task snapping to split size. The empty space between this and the top is to allow
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 36d0a3c..a353db7 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -155,6 +155,8 @@
<string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
<!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] -->
<string name="bubble_dismiss_text">Dismiss bubble</string>
+ <!-- Text used to move the bubble to fullscreen. [CHAR LIMIT=30] -->
+ <string name="bubble_fullscreen_text">Move to fullscreen</string>
<!-- Button text to stop a conversation from bubbling [CHAR LIMIT=60]-->
<string name="bubbles_dont_bubble_conversation">Don\u2019t bubble conversation</string>
<!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
index eec2468..7086691 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.graphics.Point
import android.graphics.RectF
@@ -23,9 +23,9 @@
import androidx.core.animation.Animator
import androidx.core.animation.AnimatorListenerAdapter
import androidx.core.animation.ObjectAnimator
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
/**
* Base class for common logic shared between different bubble views to support pinning bubble bar
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
similarity index 93%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
index 3c5beeb..4fe7611 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
parcelable BubbleBarLocation;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
index f0bdfde..191875d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.os.Parcel
import android.os.Parcelable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
index ec3c601..5bde1e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
similarity index 89%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
index 0329b8d..3396bc4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
/**
* Constants shared between bubbles in shell & things we have to do for bubbles in launcher.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
similarity index 92%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
index 829af08..5876682 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,10 +48,11 @@
@Nullable
private String mAppName;
private boolean mIsImportantConversation;
+ private boolean mShowAppBadge;
public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
int userId, String packageName, @Nullable String title, @Nullable String appName,
- boolean isImportantConversation) {
+ boolean isImportantConversation, boolean showAppBadge) {
mKey = key;
mFlags = flags;
mShortcutId = shortcutId;
@@ -61,6 +62,7 @@
mTitle = title;
mAppName = appName;
mIsImportantConversation = isImportantConversation;
+ mShowAppBadge = showAppBadge;
}
private BubbleInfo(Parcel source) {
@@ -73,6 +75,7 @@
mTitle = source.readString();
mAppName = source.readString();
mIsImportantConversation = source.readBoolean();
+ mShowAppBadge = source.readBoolean();
}
public String getKey() {
@@ -115,6 +118,10 @@
return mIsImportantConversation;
}
+ public boolean showAppBadge() {
+ return mShowAppBadge;
+ }
+
/**
* Whether this bubble is currently being hidden from the stack.
*/
@@ -172,6 +179,7 @@
parcel.writeString(mTitle);
parcel.writeString(mAppName);
parcel.writeBoolean(mIsImportantConversation);
+ parcel.writeBoolean(mShowAppBadge);
}
@NonNull
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
index 887af17..8681acf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.annotation.ColorInt
import android.graphics.Canvas
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
similarity index 95%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
index 444fbf7..802d7d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.content.Context
import android.graphics.Rect
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
index 7c5bb21..0c05156 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.content.Context;
import android.content.res.Configuration;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
index e06de9e..2bb66b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.animation.ObjectAnimator
import android.content.Context
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
index 4e55ba2..b1f4e33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.graphics.PointF
import android.view.MotionEvent
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
similarity index 94%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
index f90591b..c83696c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.os.Parcel;
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
index 424d4bf..b5d63bd 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -49,6 +49,7 @@
SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
DISABLE_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false),
+ SCALED_RESIZING(Flags::enableWindowingScaledResizing, false),
ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
similarity index 97%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
index 9999f08..a06cf78 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.navigationbar;
+package com.android.wm.shell.shared.handles;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -194,7 +194,7 @@
ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl();
SurfaceControl stopLayerControl = null;
if (viewRootImpl != null) {
- stopLayerControl = viewRootImpl.getSurfaceControl();
+ stopLayerControl = viewRootImpl.getSurfaceControl();
}
if (stopLayerControl == null || !stopLayerControl.isValid()) {
if (!mWaitingOnDraw) {
@@ -329,7 +329,7 @@
/**
* Get the sampled region of interest from the sampled view
* @param sampledView The view that this helper is attached to for convenience
- * @return the region to be sampled in sceen coordinates. Return {@code null} to avoid
+ * @return the region to be sampled in screen coordinates. Return {@code null} to avoid
* sampling in this frame
*/
Rect getSampledRegion(View sampledView);
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
index cf39415..6c83d88 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
@@ -29,7 +29,6 @@
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.window.TaskSnapshot;
/**
@@ -75,7 +74,7 @@
public PipColorOverlay(Context context) {
mContext = context;
- mLeash = new SurfaceControl.Builder(new SurfaceSession())
+ mLeash = new SurfaceControl.Builder()
.setCallsite(TAG)
.setName(LAYER_NAME)
.setColorLayer()
@@ -123,7 +122,7 @@
public PipSnapshotOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
mSnapshot = snapshot;
mSourceRectHint = new Rect(sourceRectHint);
- mLeash = new SurfaceControl.Builder(new SurfaceSession())
+ mLeash = new SurfaceControl.Builder()
.setCallsite(TAG)
.setName(LAYER_NAME)
.build();
@@ -183,7 +182,7 @@
mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
prepareAppIconOverlay(appIcon);
- mLeash = new SurfaceControl.Builder(new SurfaceSession())
+ mLeash = new SurfaceControl.Builder()
.setCallsite(TAG)
.setName(LAYER_NAME)
.build();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
new file mode 100644
index 0000000..05ce361
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+@file:JvmName("AppToWebUtils")
+
+package com.android.wm.shell.apptoweb
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+
+private val browserIntent = Intent()
+ .setAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse("http:"))
+
+/**
+ * Returns a boolean indicating whether a given package is a browser app.
+ */
+fun isBrowserApp(context: Context, packageName: String, userId: Int): Boolean {
+ browserIntent.setPackage(packageName)
+ val list = context.packageManager.queryIntentActivitiesAsUser(
+ browserIntent, PackageManager.MATCH_ALL, userId
+ )
+
+ list.forEach {
+ if (it.activityInfo != null && it.handleAllWebDataURI) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt
new file mode 100644
index 0000000..249185e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.apptoweb
+
+import android.app.ActivityTaskManager
+import android.app.IActivityTaskManager
+import android.app.IAssistDataReceiver
+import android.app.assist.AssistContent
+import android.content.Context
+import android.graphics.Bitmap
+import android.os.Bundle
+import android.os.RemoteException
+import android.util.Slog
+import java.lang.ref.WeakReference
+import java.util.Collections
+import java.util.WeakHashMap
+import java.util.concurrent.Executor
+
+/**
+ * Can be used to request the AssistContent from a provided task id, useful for getting the web uri
+ * if provided from the task.
+ */
+class AssistContentRequester(
+ context: Context,
+ private val callBackExecutor: Executor,
+ private val systemInteractionExecutor: Executor
+) {
+ interface Callback {
+ // Called when the [AssistContent] of the requested task is available.
+ fun onAssistContentAvailable(assistContent: AssistContent?)
+ }
+
+ private val activityTaskManager: IActivityTaskManager = ActivityTaskManager.getService()
+ private val attributionTag: String? = context.attributionTag
+ private val packageName: String = context.applicationContext.packageName
+
+ // If system loses the callback, our internal cache of original callback will also get cleared.
+ private val pendingCallbacks = Collections.synchronizedMap(WeakHashMap<Any, Callback>())
+
+ /**
+ * Request the [AssistContent] from the task with the provided id.
+ *
+ * @param taskId to query for the content.
+ * @param callback to call when the content is available, called on the main thread.
+ */
+ fun requestAssistContent(taskId: Int, callback: Callback) {
+ // ActivityTaskManager interaction here is synchronous, so call off the main thread.
+ systemInteractionExecutor.execute {
+ try {
+ val success = activityTaskManager.requestAssistDataForTask(
+ AssistDataReceiver(callback, this),
+ taskId,
+ packageName,
+ attributionTag,
+ false /* fetchStructure */
+ )
+ if (!success) {
+ executeOnMainExecutor { callback.onAssistContentAvailable(null) }
+ }
+ } catch (e: RemoteException) {
+ Slog.e(TAG, "Requesting assist content failed for task: $taskId", e)
+ }
+ }
+ }
+
+ private fun executeOnMainExecutor(callback: Runnable) {
+ callBackExecutor.execute(callback)
+ }
+
+ private class AssistDataReceiver(
+ callback: Callback,
+ parent: AssistContentRequester
+ ) : IAssistDataReceiver.Stub() {
+ // The AssistDataReceiver binder callback object is passed to a system server, that may
+ // keep hold of it for longer than the lifetime of the AssistContentRequester object,
+ // potentially causing a memory leak. In the callback passed to the system server, only
+ // keep a weak reference to the parent object and lookup its callback if it still exists.
+ private val parentRef: WeakReference<AssistContentRequester>
+ private val callbackKey = Any()
+
+ init {
+ parent.pendingCallbacks[callbackKey] = callback
+ parentRef = WeakReference(parent)
+ }
+
+ override fun onHandleAssistData(data: Bundle?) {
+ val content = data?.getParcelable(ASSIST_KEY_CONTENT, AssistContent::class.java)
+ if (content == null) {
+ Slog.d(TAG, "Received AssistData, but no AssistContent found")
+ return
+ }
+ val requester = parentRef.get()
+ if (requester != null) {
+ val callback = requester.pendingCallbacks[callbackKey]
+ if (callback != null) {
+ requester.executeOnMainExecutor { callback.onAssistContentAvailable(content) }
+ } else {
+ Slog.d(TAG, "Callback received after calling UI was disposed of")
+ }
+ } else {
+ Slog.d(TAG, "Callback received after Requester was collected")
+ }
+ }
+
+ override fun onHandleAssistScreenshot(screenshot: Bitmap) {}
+ }
+
+ companion object {
+ private const val TAG = "AssistContentRequester"
+ private const val ASSIST_KEY_CONTENT = "content"
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
index bfe1306a..6207e5b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
@@ -1,6 +1,8 @@
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 33949f5..f478b44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -74,6 +74,7 @@
import android.window.IOnBackInvokedCallback;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
@@ -572,8 +573,14 @@
private void startBackNavigation(@NonNull BackTouchTracker touchTracker) {
try {
startLatencyTracking();
+ final BackAnimationAdapter adapter = mEnableAnimations.get()
+ ? mBackAnimationAdapter : null;
+ if (adapter != null && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) {
+ adapter.updateSupportedAnimators(
+ mShellBackAnimationRegistry.getSupportedAnimators());
+ }
mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
- mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
+ mNavigationObserver, adapter);
onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
} catch (RemoteException remoteException) {
Log.e(TAG, "Failed to initAnimation", remoteException);
@@ -867,10 +874,6 @@
// start post animation
dispatchOnBackInvoked(mActiveCallback);
} else {
- if (migrateBackToTransition
- && mBackTransitionHandler.mPrepareOpenTransition != null) {
- mBackTransitionHandler.createClosePrepareTransition();
- }
tryDispatchOnBackCancelled(mActiveCallback);
}
}
@@ -975,7 +978,6 @@
mShellBackAnimationRegistry.resetDefaultCrossActivity();
cancelLatencyTracking();
mReceivedNullNavigationInfo = false;
- mBackTransitionHandler.mLastTrigger = triggerBack;
if (mBackNavigationInfo != null) {
mPreviousNavigationType = mBackNavigationInfo.getType();
mBackNavigationInfo.onBackNavigationFinished(triggerBack);
@@ -1096,7 +1098,6 @@
endLatencyTracking();
if (!validateAnimationTargets(apps)) {
Log.e(TAG, "Invalid animation targets!");
- mBackTransitionHandler.consumeQueuedTransitionIfNeeded();
return;
}
mBackAnimationFinishedCallback = finishedCallback;
@@ -1106,7 +1107,6 @@
return;
}
kickStartAnimation();
- mBackTransitionHandler.consumeQueuedTransitionIfNeeded();
});
}
@@ -1114,7 +1114,6 @@
public void onAnimationCancelled() {
mShellExecutor.execute(
() -> {
- mBackTransitionHandler.consumeQueuedTransitionIfNeeded();
if (!mShellBackAnimationRegistry.cancel(
mBackNavigationInfo != null
? mBackNavigationInfo.getType()
@@ -1153,8 +1152,6 @@
boolean mCloseTransitionRequested;
SurfaceControl.Transaction mFinishOpenTransaction;
Transitions.TransitionFinishCallback mFinishOpenTransitionCallback;
- QueuedTransition mQueuedTransition = null;
- boolean mLastTrigger;
// The Transition to make behindActivity become visible
IBinder mPrepareOpenTransition;
// The Transition to make behindActivity become invisible, if prepare open exist and
@@ -1162,8 +1159,8 @@
IBinder mClosePrepareTransition;
TransitionInfo mOpenTransitionInfo;
void onAnimationFinished() {
- if (!mCloseTransitionRequested && mClosePrepareTransition == null) {
- applyFinishOpenTransition();
+ if (!mCloseTransitionRequested && mPrepareOpenTransition != null) {
+ createClosePrepareTransition();
}
if (mOnAnimationFinishCallback != null) {
mOnAnimationFinishCallback.run();
@@ -1171,24 +1168,19 @@
}
}
- void consumeQueuedTransitionIfNeeded() {
- if (mQueuedTransition != null) {
- mQueuedTransition.consume();
- mQueuedTransition = null;
- }
- }
-
private void applyFinishOpenTransition() {
- if (mFinishOpenTransaction != null) {
- mFinishOpenTransaction.apply();
- mFinishOpenTransaction = null;
- }
- if (mFinishOpenTransitionCallback != null) {
- mFinishOpenTransitionCallback.onTransitionFinished(null);
- mFinishOpenTransitionCallback = null;
- }
mOpenTransitionInfo = null;
mPrepareOpenTransition = null;
+ if (mFinishOpenTransaction != null) {
+ final SurfaceControl.Transaction t = mFinishOpenTransaction;
+ mFinishOpenTransaction = null;
+ t.apply();
+ }
+ if (mFinishOpenTransitionCallback != null) {
+ final Transitions.TransitionFinishCallback callback = mFinishOpenTransitionCallback;
+ mFinishOpenTransitionCallback = null;
+ callback.onTransitionFinished(null);
+ }
}
private void applyAndFinish(@NonNull SurfaceControl.Transaction st,
@@ -1206,7 +1198,9 @@
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
+ final boolean isPrepareTransition =
+ info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
+ if (isPrepareTransition) {
kickStartAnimation();
}
// Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't
@@ -1231,21 +1225,14 @@
}
if (mApps == null || mApps.length == 0) {
- if (mBackNavigationInfo != null && mShellBackAnimationRegistry
- .isWaitingAnimation(mBackNavigationInfo.getType())) {
- // Waiting for animation? Queue update to wait for animation start.
- consumeQueuedTransitionIfNeeded();
- mQueuedTransition = new QueuedTransition(info, st, ft, finishCallback);
- return true;
- } else if (mLastTrigger) {
- // animation was done, consume directly
+ if (mCloseTransitionRequested) {
+ // animation never start, consume directly
applyAndFinish(st, ft, finishCallback);
return true;
- } else {
- // animation was cancelled but transition haven't happen, we must handle it
- if (mClosePrepareTransition == null && mCurrentTracker.isFinished()) {
- createClosePrepareTransition();
- }
+ } else if (mClosePrepareTransition == null && isPrepareTransition) {
+ // Gesture animation was cancelled before prepare transition ready, create
+ // the close prepare transition
+ createClosePrepareTransition();
}
}
@@ -1256,6 +1243,10 @@
}
void createClosePrepareTransition() {
+ if (mClosePrepareTransition != null) {
+ Log.e(TAG, "Re-create close prepare transition");
+ return;
+ }
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.restoreBackNavi();
mClosePrepareTransition = mTransitions.startTransition(
@@ -1272,19 +1263,24 @@
ComponentName openComponent = null;
int tmpSize;
int openTaskId = INVALID_TASK_ID;
+ WindowContainerToken openToken = null;
for (int j = init.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = init.getChanges().get(j);
if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
openComponent = findComponentName(change);
openTaskId = findTaskId(change);
+ openToken = findToken(change);
if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
openShowWallpaper = true;
}
break;
}
}
- if (openComponent == null && openTaskId == INVALID_TASK_ID) {
- // shouldn't happen.
+ if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) {
+ // This shouldn't happen, but if that happen, consume the initial transition anyway.
+ Log.e(TAG, "Unable to merge following transition, cannot find the gesture "
+ + "animated target from the open transition=" + mOpenTransitionInfo);
+ mOpenTransitionInfo = null;
return;
}
// find first non-prepare open target
@@ -1315,7 +1311,7 @@
boolean moveToTop = false;
for (int j = info.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isSameChangeTarget(openComponent, openTaskId, change)) {
+ if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))
@@ -1328,8 +1324,11 @@
tmpSize = init.getChanges().size();
for (int i = 0; i < tmpSize; ++i) {
final TransitionInfo.Change change = init.getChanges().get(i);
+ if (change.hasFlags(FLAG_IS_WALLPAPER)) {
+ continue;
+ }
if (moveToTop) {
- if (isSameChangeTarget(openComponent, openTaskId, change)) {
+ if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
}
}
@@ -1358,7 +1357,7 @@
if (nonBackClose && nonBackOpen) {
for (int j = info.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isSameChangeTarget(openComponent, openTaskId, change)) {
+ if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
info.getChanges().remove(j);
@@ -1368,6 +1367,8 @@
}
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation transition, merge pending "
+ "transitions result=%s", info);
+ // Only handle one merge transition request.
+ mOpenTransitionInfo = null;
}
@Override
@@ -1378,16 +1379,22 @@
mClosePrepareTransition = null;
}
// try to handle unexpected transition
- mergePendingTransitions(info);
+ if (mOpenTransitionInfo != null) {
+ mergePendingTransitions(info);
+ }
+ if (info.getType() == TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION
+ && !mCloseTransitionRequested && info.getChanges().isEmpty() && mApps == null) {
+ finishCallback.onTransitionFinished(null);
+ t.apply();
+ applyFinishOpenTransition();
+ return;
+ }
if (isNotGestureBackTransition(info) || shouldCancelAnimation(info)
|| !mCloseTransitionRequested) {
if (mPrepareOpenTransition != null) {
applyFinishOpenTransition();
}
- if (mQueuedTransition != null) {
- consumeQueuedTransitionIfNeeded();
- }
return;
}
// Handle the commit transition if this handler is running the open transition.
@@ -1395,11 +1402,9 @@
t.apply();
if (mCloseTransitionRequested) {
if (mApps == null || mApps.length == 0) {
- if (mQueuedTransition == null) {
- // animation was done
- applyFinishOpenTransition();
- mCloseTransitionRequested = false;
- } // let queued transition finish.
+ // animation was done
+ applyFinishOpenTransition();
+ mCloseTransitionRequested = false;
} else {
// we are animating, wait until animation finish
mOnAnimationFinishCallback = () -> {
@@ -1487,15 +1492,28 @@
}
}
if (openingLeash != null) {
+ int rootIdx = -1;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.hasFlags(FLAG_IS_WALLPAPER)) {
+ st.setAlpha(c.getLeash(), 1.0f);
+ continue;
+ }
if (TransitionUtil.isOpeningMode(c.getMode())) {
final Point offset = c.getEndRelOffset();
st.setPosition(c.getLeash(), offset.x, offset.y);
st.reparent(c.getLeash(), openingLeash);
st.setAlpha(c.getLeash(), 1.0f);
+ rootIdx = TransitionUtil.rootIndexFor(c, info);
}
}
+ // The root leash and the leash of opening target should actually in the same level,
+ // but since the root leash is created after opening target, it will have higher
+ // layer in surface flinger. Move the root leash to lower level, so it won't affect
+ // the playing animation.
+ if (rootIdx >= 0 && info.getRootCount() > 0) {
+ st.setLayer(info.getRoot(rootIdx).getLeash(), -1);
+ }
}
st.apply();
mFinishOpenTransaction = ft;
@@ -1538,6 +1556,10 @@
if (openingLeash != null && closingLeash != null) {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.hasFlags(FLAG_IS_WALLPAPER)) {
+ st.setAlpha(c.getLeash(), 1.0f);
+ continue;
+ }
if (TransitionUtil.isOpeningMode(c.getMode())) {
final Point offset = c.getEndRelOffset();
st.setPosition(c.getLeash(), offset.x, offset.y);
@@ -1576,41 +1598,6 @@
}
return null;
}
-
- class QueuedTransition {
- final TransitionInfo mInfo;
- final SurfaceControl.Transaction mSt;
- final SurfaceControl.Transaction mFt;
- final Transitions.TransitionFinishCallback mFinishCallback;
- QueuedTransition(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction st,
- @NonNull SurfaceControl.Transaction ft,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- mInfo = info;
- mSt = st;
- mFt = ft;
- mFinishCallback = finishCallback;
- }
-
- void consume() {
- // not animating, consume transition directly
- if (mApps == null || mApps.length == 0) {
- applyAndFinish(mSt, mFt, mFinishCallback);
- return;
- }
- // we are animating
- if (handlePrepareTransition(mInfo, mSt, mFt, mFinishCallback)) {
- // handle merge transition if any
- if (mCloseTransitionRequested) {
- mOnAnimationFinishCallback = () -> {
- applyFinishOpenTransition();
- mCloseTransitionRequested = false;
- };
- }
- }
- handleCloseTransition(mInfo, mSt, mFt, mFinishCallback);
- }
- }
}
private static boolean isNotGestureBackTransition(@NonNull TransitionInfo info) {
@@ -1628,6 +1615,10 @@
return false;
}
+ private static WindowContainerToken findToken(TransitionInfo.Change change) {
+ return change.getContainer();
+ }
+
private static ComponentName findComponentName(TransitionInfo.Change change) {
final ComponentName componentName = change.getActivityComponent();
if (componentName != null) {
@@ -1649,11 +1640,13 @@
}
private static boolean isSameChangeTarget(ComponentName topActivity, int taskId,
- TransitionInfo.Change change) {
+ WindowContainerToken token, TransitionInfo.Change change) {
final ComponentName openChange = findComponentName(change);
final int firstTaskId = findTaskId(change);
+ final WindowContainerToken openToken = findToken(change);
return (openChange != null && openChange == topActivity)
- || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId);
+ || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId)
+ || (openToken != null && token == openToken);
}
private static boolean canBeTransitionTarget(TransitionInfo.Change change) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
index 6fafa75..ae2c7b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
@@ -23,6 +23,8 @@
import android.util.SparseArray;
import android.window.BackNavigationInfo;
+import java.util.ArrayList;
+
/** Registry for all types of default back animations */
public class ShellBackAnimationRegistry {
private static final String TAG = "ShellBackPreview";
@@ -31,6 +33,8 @@
private ShellBackAnimation mDefaultCrossActivityAnimation;
private final ShellBackAnimation mCustomizeActivityAnimation;
private final ShellBackAnimation mCrossTaskAnimation;
+ private boolean mSupportedAnimatorsChanged = false;
+ private final ArrayList<Integer> mSupportedAnimators = new ArrayList<>();
public ShellBackAnimationRegistry(
@ShellBackAnimation.CrossActivity @Nullable ShellBackAnimation crossActivityAnimation,
@@ -60,7 +64,7 @@
mDefaultCrossActivityAnimation = crossActivityAnimation;
mCustomizeActivityAnimation = customizeActivityAnimation;
mCrossTaskAnimation = crossTaskAnimation;
-
+ updateSupportedAnimators();
// TODO(b/236760237): register dialog close animation when it's completed.
}
@@ -71,6 +75,7 @@
if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
mDefaultCrossActivityAnimation = null;
}
+ updateSupportedAnimators();
}
void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
@@ -79,6 +84,24 @@
if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
mDefaultCrossActivityAnimation = null;
}
+ updateSupportedAnimators();
+ }
+
+ private void updateSupportedAnimators() {
+ mSupportedAnimators.clear();
+ for (int i = mAnimationDefinition.size() - 1; i >= 0; --i) {
+ mSupportedAnimators.add(mAnimationDefinition.keyAt(i));
+ }
+ mSupportedAnimatorsChanged = true;
+ }
+
+ boolean hasSupportedAnimatorsChanged() {
+ return mSupportedAnimatorsChanged;
+ }
+
+ ArrayList<Integer> getSupportedAnimators() {
+ mSupportedAnimatorsChanged = false;
+ return mSupportedAnimators;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 3e758bb..169361a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -53,9 +53,9 @@
import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
import java.io.PrintWriter;
import java.util.List;
@@ -349,7 +349,8 @@
getPackageName(),
getTitle(),
getAppName(),
- isImportantConversation());
+ isImportantConversation(),
+ !isAppLaunchIntent());
}
@Override
@@ -608,7 +609,8 @@
callback.onBubbleViewsReady(bubble);
}
},
- mMainExecutor);
+ mMainExecutor,
+ mBgExecutor);
if (mInflateSynchronously) {
mInflationTaskLegacy.onPostExecute(mInflationTaskLegacy.doInBackground());
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index b508c1b..c545d73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -104,14 +104,14 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 4ad1802..709a7bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -41,10 +41,10 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubbles.DismissReason;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
-import com.android.wm.shell.common.bubbles.RemovedBubble;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.RemovedBubble;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index 4e80e90..ec4854b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.bubbles
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/** Manager interface for bubble expanded views. */
interface BubbleExpandedViewManager {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index f32974e..68c4657 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -80,7 +80,10 @@
expandedViewManager,
positioner,
/* isOverflow= */ true,
- /* bubbleTaskView= */ null
+ /* bubbleTaskView= */ null,
+ /* mainExecutor= */ null,
+ /* backgroundExecutor= */ null,
+ /* regionSamplingProvider= */ null
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
index bdb09e1..fd110a276 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
@@ -17,8 +17,8 @@
import android.graphics.Color
import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.BubblePopupDrawable
-import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
+import com.android.wm.shell.shared.bubbles.BubblePopupView
/**
* A convenience method to setup the [BubblePopupView] with the correct config using local resources
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 0cf187b..c386c93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -32,7 +32,7 @@
import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Keeps track of display size, configuration, and specific bubble sizes. One place for all
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 53bbf88..2795881 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -24,10 +24,10 @@
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.LEFT;
import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.RIGHT;
-import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -91,8 +91,8 @@
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.bubbles.RelativeTouchListener;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 5f8f0fd..0c0fd7b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -60,6 +60,9 @@
/** Called when back is pressed on the task root. */
void onBackPressed();
+
+ /** Called when task removal has started. */
+ void onTaskRemovalStarted();
}
private final Context mContext;
@@ -190,6 +193,7 @@
((ViewGroup) mParentView).removeView(mTaskView);
mTaskView = null;
}
+ mListener.onTaskRemovalStarted();
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 13855f7..3982a23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -38,6 +38,7 @@
import android.util.Log;
import android.util.PathParser;
import android.view.LayoutInflater;
+import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -47,6 +48,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -222,7 +224,16 @@
ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s",
mBubble.getKey());
viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(),
- mPositioner.get(), false /* isOverflow */, viewInfo.taskView);
+ mPositioner.get(), false /* isOverflow */, viewInfo.taskView,
+ mMainExecutor, mBgExecutor, new RegionSamplingProvider() {
+ @Override
+ public RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor, Executor mainExecutor) {
+ return RegionSamplingProvider.super.createHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+ });
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
index 5cfebf8..1b7bb0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -38,6 +38,7 @@
import android.util.Log;
import android.util.PathParser;
import android.view.LayoutInflater;
+import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -46,6 +47,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -85,6 +87,7 @@
private boolean mSkipInflation;
private Callback mCallback;
private Executor mMainExecutor;
+ private Executor mBackgroundExecutor;
/**
* Creates a task to load information for the provided {@link Bubble}. Once all info
@@ -100,7 +103,8 @@
BubbleIconFactory factory,
boolean skipInflation,
Callback c,
- Executor mainExecutor) {
+ Executor mainExecutor,
+ Executor backgroundExecutor) {
mBubble = b;
mContext = new WeakReference<>(context);
mExpandedViewManager = new WeakReference<>(expandedViewManager);
@@ -112,6 +116,7 @@
mSkipInflation = skipInflation;
mCallback = c;
mMainExecutor = mainExecutor;
+ mBackgroundExecutor = backgroundExecutor;
}
@Override
@@ -123,7 +128,7 @@
if (mLayerView.get() != null) {
return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
- mBubble, mSkipInflation);
+ mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor);
} else {
return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
@@ -188,7 +193,9 @@
BubbleBarLayerView layerView,
BubbleIconFactory iconFactory,
Bubble b,
- boolean skipInflation) {
+ boolean skipInflation,
+ Executor mainExecutor,
+ Executor backgroundExecutor) {
BubbleViewInfo info = new BubbleViewInfo();
if (!skipInflation && !b.isInflated()) {
@@ -197,7 +204,16 @@
info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
info.bubbleBarExpandedView.initialize(
- expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView);
+ expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView,
+ mainExecutor, backgroundExecutor, new RegionSamplingProvider() {
+ @Override
+ public RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor, Executor mainExecutor) {
+ return RegionSamplingProvider.super.createHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+ });
}
if (!populateCommonInfo(info, c, b, iconFactory)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 9a27fb6..62895fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -38,9 +38,9 @@
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
index 48692d4..00a8172 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
@@ -18,7 +18,7 @@
package com.android.wm.shell.bubbles
import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.DismissView
fun DismissView.setup() {
setup(DismissView.Config(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 5779a8f..1855b93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -20,7 +20,7 @@
import android.graphics.Rect;
import android.content.pm.ShortcutInfo;
import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Interface that is exposed to remote callers (launcher) to manipulate the bubbles feature when
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
index 14d29cd..eb907db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
@@ -17,7 +17,7 @@
package com.android.wm.shell.bubbles;
import android.os.Bundle;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Listener interface that Launcher attaches to SystemUI to get bubbles callbacks.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java
new file mode 100644
index 0000000..30f5c8fd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java
@@ -0,0 +1,40 @@
+/*
+ * 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.bubbles;
+
+import android.view.View;
+
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Wrapper to provide a {@link com.android.wm.shell.shared.handles.RegionSamplingHelper} to allow
+ * testing it.
+ */
+public interface RegionSamplingProvider {
+
+ /** Creates and returns the region sampling helper */
+ default RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor,
+ Executor mainExecutor) {
+ return new RegionSamplingHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 565fde0..74c3748 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -253,6 +253,7 @@
return;
}
setDragPivot(bbev);
+ bbev.setDragging(true);
// Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius
final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_DRAG_SCALE;
@@ -329,6 +330,7 @@
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
bbev.resetPivot();
+ bbev.setDragging(false);
}
});
startNewDragAnimation(animatorSet);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 6d868d2..ec235a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -19,10 +19,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -37,6 +34,7 @@
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
@@ -46,9 +44,12 @@
import com.android.wm.shell.bubbles.BubbleTaskView;
import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.bubbles.RegionSamplingProvider;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import com.android.wm.shell.taskview.TaskView;
+import java.util.concurrent.Executor;
import java.util.function.Supplier;
/** Expanded view of a bubble when it's part of the bubble bar. */
@@ -92,16 +93,35 @@
private boolean mIsOverflow;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
private BubbleBarMenuViewController mMenuViewController;
- private @Nullable Supplier<Rect> mLayerBoundsSupplier;
- private @Nullable Listener mListener;
+ @Nullable
+ private Supplier<Rect> mLayerBoundsSupplier;
+ @Nullable
+ private Listener mListener;
private BubbleBarHandleView mHandleView;
- private @Nullable TaskView mTaskView;
- private @Nullable BubbleOverflowContainerView mOverflowView;
+ @Nullable
+ private TaskView mTaskView;
+ @Nullable
+ private BubbleOverflowContainerView mOverflowView;
+ /**
+ * The handle shown in the caption area is tinted based on the background color of the area.
+ * This can vary so we sample the caption region and update the handle color based on that.
+ * If we're showing the overflow, the helper and executors will be null.
+ */
+ @Nullable
+ private RegionSamplingHelper mRegionSamplingHelper;
+ @Nullable
+ private RegionSamplingProvider mRegionSamplingProvider;
+ @Nullable
+ private Executor mMainExecutor;
+ @Nullable
+ private Executor mBackgroundExecutor;
+ private final Rect mSampleRect = new Rect();
+ private final int[] mLoc = new int[2];
+
+ /** Height of the caption inset at the top of the TaskView */
private int mCaptionHeight;
-
- private int mBackgroundColor;
/** Corner radius used when view is resting */
private float mRestingCornerRadius = 0f;
/** Corner radius applied while dragging */
@@ -116,6 +136,7 @@
*/
private boolean mIsContentVisible = false;
private boolean mIsAnimating;
+ private boolean mIsDragging;
public BubbleBarExpandedView(Context context) {
this(context, null);
@@ -154,21 +175,20 @@
setOnTouchListener((v, event) -> true);
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- // Hide manage menu when view disappears
- mMenuViewController.hideMenu(false /* animated */);
- }
-
/** Initializes the view, must be called before doing anything else. */
public void initialize(BubbleExpandedViewManager expandedViewManager,
BubblePositioner positioner,
boolean isOverflow,
- @Nullable BubbleTaskView bubbleTaskView) {
+ @Nullable BubbleTaskView bubbleTaskView,
+ @Nullable Executor mainExecutor,
+ @Nullable Executor backgroundExecutor,
+ @Nullable RegionSamplingProvider regionSamplingProvider) {
mManager = expandedViewManager;
mPositioner = positioner;
mIsOverflow = isOverflow;
+ mMainExecutor = mainExecutor;
+ mBackgroundExecutor = backgroundExecutor;
+ mRegionSamplingProvider = regionSamplingProvider;
if (mIsOverflow) {
mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
@@ -191,6 +211,7 @@
mTaskView.setEnableSurfaceClipping(true);
mTaskView.setCornerRadius(mCurrentCornerRadius);
mTaskView.setVisibility(VISIBLE);
+ mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
// Handle view needs to draw on top of task view.
bringChildToFront(mHandleView);
@@ -228,6 +249,13 @@
public void onDismissBubble(Bubble bubble) {
mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE);
}
+
+ @Override
+ public void onMoveToFullscreen(Bubble bubble) {
+ if (mTaskView != null) {
+ mTaskView.moveToFullscreen();
+ }
+ }
});
mHandleView.setOnClickListener(view -> {
mMenuViewController.showMenu(true /* animated */);
@@ -238,32 +266,40 @@
return mHandleView;
}
- // TODO (b/275087636): call this when theme/config changes
/** Updates the view based on the current theme. */
public void applyThemeAttrs() {
+ mCaptionHeight = getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_caption_height);
mRestingCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_corner_radius
- );
+ R.dimen.bubble_bar_expanded_view_corner_radius);
mDraggedCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_corner_radius_dragged
- );
+ R.dimen.bubble_bar_expanded_view_corner_radius_dragged);
mCurrentCornerRadius = mRestingCornerRadius;
- final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- android.R.attr.colorBackgroundFloating});
- mBackgroundColor = ta.getColor(0, Color.WHITE);
- ta.recycle();
- mCaptionHeight = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_caption_height);
-
if (mTaskView != null) {
mTaskView.setCornerRadius(mCurrentCornerRadius);
- updateHandleColor(true /* animated */);
+ mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
}
}
@Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ // Hide manage menu when view disappears
+ mMenuViewController.hideMenu(false /* animated */);
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ recreateRegionSamplingHelper();
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mTaskView != null) {
@@ -277,16 +313,13 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mTaskView != null) {
- mTaskView.layout(l, t, r,
- t + mTaskView.getMeasuredHeight());
- mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
+ mTaskView.layout(l, t, r, t + mTaskView.getMeasuredHeight());
}
}
@Override
public void onTaskCreated() {
setContentVisibility(true);
- updateHandleColor(false /* animated */);
if (mListener != null) {
mListener.onTaskCreated();
}
@@ -298,11 +331,70 @@
}
@Override
+ public void onTaskRemovalStarted() {
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+ }
+
+ @Override
public void onBackPressed() {
if (mListener == null) return;
mListener.onBackPressed();
}
+ /**
+ * Set whether this view is currently being dragged.
+ *
+ * When dragging, the handle is hidden and content shouldn't be sampled. When dragging has
+ * ended we should start again.
+ */
+ public void setDragging(boolean isDragging) {
+ if (isDragging != mIsDragging) {
+ mIsDragging = isDragging;
+ updateSamplingState();
+ }
+ }
+
+ /** Returns whether region sampling should be enabled, i.e. if task view content is visible. */
+ private boolean shouldSampleRegion() {
+ return mTaskView != null
+ && mTaskView.getTaskInfo() != null
+ && !mIsDragging
+ && !mIsAnimating
+ && mIsContentVisible;
+ }
+
+ /**
+ * Handles starting or stopping the region sampling helper based on
+ * {@link #shouldSampleRegion()}.
+ */
+ private void updateSamplingState() {
+ if (mRegionSamplingHelper == null) return;
+ boolean shouldSample = shouldSampleRegion();
+ if (shouldSample) {
+ mRegionSamplingHelper.start(getCaptionSampleRect());
+ } else {
+ mRegionSamplingHelper.stop();
+ }
+ }
+
+ /** Returns the current area of the caption bar, in screen coordinates. */
+ Rect getCaptionSampleRect() {
+ if (mTaskView == null) return null;
+ mTaskView.getLocationOnScreen(mLoc);
+ mSampleRect.set(mLoc[0], mLoc[1],
+ mLoc[0] + mTaskView.getWidth(),
+ mLoc[1] + mCaptionHeight);
+ return mSampleRect;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ public RegionSamplingHelper getRegionSamplingHelper() {
+ return mRegionSamplingHelper;
+ }
+
/** Cleans up the expanded view, should be called when the bubble is no longer active. */
public void cleanUpExpandedState() {
mMenuViewController.hideMenu(false /* animated */);
@@ -387,27 +479,14 @@
if (!mIsAnimating) {
mTaskView.setAlpha(visible ? 1f : 0f);
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.setWindowVisible(visible);
+ }
+ updateSamplingState();
}
}
/**
- * Updates the handle color based on the task view status bar or background color; if those
- * are transparent it defaults to the background color pulled from system theme attributes.
- */
- private void updateHandleColor(boolean animated) {
- if (mTaskView == null || mTaskView.getTaskInfo() == null) return;
- int color = mBackgroundColor;
- ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription;
- if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) {
- color = taskDescription.getStatusBarColor();
- } else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) {
- color = taskDescription.getBackgroundColor();
- }
- final boolean isRegionDark = Color.luminance(color) <= 0.5;
- mHandleView.updateHandleColor(isRegionDark, animated);
- }
-
- /**
* Sets the alpha of both this view and the task view.
*/
public void setTaskViewAlpha(float alpha) {
@@ -435,6 +514,11 @@
*/
public void setAnimating(boolean animating) {
mIsAnimating = animating;
+ if (mIsAnimating) {
+ // Stop sampling while animating -- when animating is done setContentVisibility will
+ // re-trigger sampling if we're visible.
+ updateSamplingState();
+ }
// If we're done animating, apply the correct visibility.
if (!animating) {
setContentVisibility(mIsContentVisible);
@@ -474,6 +558,37 @@
}
}
+ private void recreateRegionSamplingHelper() {
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+ if (mMainExecutor == null || mBackgroundExecutor == null
+ || mRegionSamplingProvider == null) {
+ // Null when it's the overflow / don't need sampling then.
+ return;
+ }
+ mRegionSamplingHelper = mRegionSamplingProvider.createHelper(this,
+ new RegionSamplingHelper.SamplingCallback() {
+ @Override
+ public void onRegionDarknessChanged(boolean isRegionDark) {
+ if (mHandleView != null) {
+ mHandleView.updateHandleColor(isRegionDark,
+ true /* animated */);
+ }
+ }
+
+ @Override
+ public Rect getSampledRegion(View sampledView) {
+ return getCaptionSampleRect();
+ }
+
+ @Override
+ public boolean isSamplingEnabled() {
+ return shouldSampleRegion();
+ }
+ }, mMainExecutor, mBackgroundExecutor);
+ }
+
private class HandleViewAccessibilityDelegate extends AccessibilityDelegate {
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull View host,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index eeb5c94..07463bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -20,8 +20,8 @@
import android.view.MotionEvent
import android.view.View
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.common.bubbles.DismissView
-import com.android.wm.shell.common.bubbles.RelativeTouchListener
+import com.android.wm.shell.shared.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject
/** Controller for handling drag interactions with [BubbleBarExpandedView] */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
index c91567d..e781c07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -42,7 +42,9 @@
private final @ColorInt int mHandleLightColor;
private final @ColorInt int mHandleDarkColor;
- private @Nullable ObjectAnimator mColorChangeAnim;
+ private @ColorInt int mCurrentColor;
+ @Nullable
+ private ObjectAnimator mColorChangeAnim;
public BubbleBarHandleView(Context context) {
this(context, null /* attrs */);
@@ -88,13 +90,17 @@
*
* @param isRegionDark Whether the background behind the handle is dark, and thus the handle
* should be light (and vice versa).
- * @param animated Whether to animate the change, or apply it immediately.
+ * @param animated Whether to animate the change, or apply it immediately.
*/
public void updateHandleColor(boolean isRegionDark, boolean animated) {
int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor;
+ if (newColor == mCurrentColor) {
+ return;
+ }
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
}
+ mCurrentColor = newColor;
if (animated) {
mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor);
mColorChangeAnim.addListener(new AnimatorListenerAdapter() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index ac42453..1c9c195 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -44,9 +44,9 @@
import com.android.wm.shell.bubbles.DeviceConfig;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedViewDragController.DragListener;
-import com.android.wm.shell.common.bubbles.BaseBubblePinController;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DismissView;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index 0d72998..5148107 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -29,6 +29,7 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
@@ -219,6 +220,21 @@
}
));
+ if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) {
+ menuActions.add(new BubbleBarMenuView.MenuAction(
+ Icon.createWithResource(resources,
+ R.drawable.desktop_mode_ic_handle_menu_fullscreen),
+ resources.getString(R.string.bubble_fullscreen_text),
+ tintColor,
+ view -> {
+ hideMenu(true /* animated */);
+ if (mListener != null) {
+ mListener.onMoveToFullscreen(bubble);
+ }
+ }
+ ));
+ }
+
return menuActions;
}
@@ -249,5 +265,10 @@
* Dismiss bubble and remove it from the bubble stack
*/
void onDismissBubble(Bubble bubble);
+
+ /**
+ * Move the bubble to fullscreen.
+ */
+ void onMoveToFullscreen(Bubble bubble);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index e108f7b..9fd255d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -34,9 +34,9 @@
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.BubbleViewProvider
import com.android.wm.shell.bubbles.setup
-import com.android.wm.shell.common.bubbles.BubblePopupDrawable
-import com.android.wm.shell.common.bubbles.BubblePopupView
import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
+import com.android.wm.shell.shared.bubbles.BubblePopupView
import kotlin.math.roundToInt
/** Manages bubble education presentation and animation */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
index 651bf02..23ba2bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
@@ -25,8 +25,8 @@
import androidx.core.view.updateLayoutParams
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/**
* Controller to manage pinning bubble bar to left or right when dragging starts from the bubble bar
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 5b01a0d..f03daad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -291,17 +291,11 @@
if (hadImeSourceControl != hasImeSourceControl) {
dispatchImeControlTargetChanged(mDisplayId, hasImeSourceControl);
}
+ final boolean hasImeLeash = hasImeSourceControl && imeSourceControl.getLeash() != null;
boolean pendingImeStartAnimation = false;
- boolean canAnimate;
- if (android.view.inputmethod.Flags.refactorInsetsController()) {
- canAnimate = hasImeSourceControl && imeSourceControl.getLeash() != null;
- } else {
- canAnimate = hasImeSourceControl;
- }
-
boolean positionChanged = false;
- if (canAnimate) {
+ if (hasImeLeash) {
if (mAnimation != null) {
final Point lastSurfacePosition = hadImeSourceControl
? mImeSourceControl.getSurfacePosition() : null;
@@ -325,6 +319,13 @@
// continue the bar to slide to the end (even without visible IME)
mAnimation.cancel();
}
+
+ // Make mImeSourceControl point to the new control before starting the animation.
+ if (hadImeSourceControl && mImeSourceControl != imeSourceControl) {
+ mImeSourceControl.release(SurfaceControl::release);
+ }
+ mImeSourceControl = imeSourceControl;
+
if (positionChanged) {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
// For showing the IME, the leash has to be available first. Hiding
@@ -338,11 +339,6 @@
}
}
- if (hadImeSourceControl && mImeSourceControl != imeSourceControl) {
- mImeSourceControl.release(SurfaceControl::release);
- }
- mImeSourceControl = imeSourceControl;
-
if (android.view.inputmethod.Flags.refactorInsetsController()) {
if (pendingImeStartAnimation) {
startAnimation(true, true /* forceRestart */);
@@ -465,11 +461,12 @@
private void startAnimation(final boolean show, final boolean forceRestart,
@NonNull final ImeTracker.Token statsToken) {
+ if (mImeSourceControl == null || mImeSourceControl.getLeash() == null) {
+ if (DEBUG) Slog.d(TAG, "No leash available, not starting the animation.");
+ return;
+ }
if (android.view.inputmethod.Flags.refactorInsetsController()) {
- if (mImeSourceControl == null || mImeSourceControl.getLeash() == null) {
- if (DEBUG) Slog.d(TAG, "No leash available, not starting the animation.");
- return;
- } else if (!mImeRequestedVisible && show) {
+ if (!mImeRequestedVisible && show) {
// we have a control with leash, but the IME was not requested visible before,
// therefore aborting the show animation.
Slog.e(TAG, "IME was not requested visible, not starting the show animation.");
@@ -478,7 +475,7 @@
}
}
final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
- if (imeSource == null || mImeSourceControl == null) {
+ if (imeSource == null) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
return;
}
@@ -515,8 +512,10 @@
}
mAnimation.cancel();
}
- final float defaultY = mImeSourceControl.getSurfacePosition().y;
- final float x = mImeSourceControl.getSurfacePosition().x;
+ final InsetsSourceControl animatingControl = new InsetsSourceControl(mImeSourceControl);
+ final SurfaceControl animatingLeash = animatingControl.getLeash();
+ final float defaultY = animatingControl.getSurfacePosition().y;
+ final float x = animatingControl.getSurfacePosition().x;
final float hiddenY = defaultY + mImeFrame.height();
final float shownY = defaultY;
final float startY = show ? hiddenY : shownY;
@@ -538,13 +537,10 @@
mAnimation.addUpdateListener(animation -> {
SurfaceControl.Transaction t = mTransactionPool.acquire();
float value = (float) animation.getAnimatedValue();
- if (!android.view.inputmethod.Flags.refactorInsetsController() || (
- mImeSourceControl != null && mImeSourceControl.getLeash() != null)) {
- t.setPosition(mImeSourceControl.getLeash(), x, value);
- final float alpha = (mAnimateAlpha || isFloating)
- ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
- t.setAlpha(mImeSourceControl.getLeash(), alpha);
- }
+ t.setPosition(animatingLeash, x, value);
+ final float alpha = (mAnimateAlpha || isFloating)
+ ? (value - hiddenY) / (shownY - hiddenY) : 1f;
+ t.setAlpha(animatingLeash, alpha);
dispatchPositionChanged(mDisplayId, imeTop(value), t);
t.apply();
mTransactionPool.release(t);
@@ -561,7 +557,7 @@
ValueAnimator valueAnimator = (ValueAnimator) animation;
float value = (float) valueAnimator.getAnimatedValue();
SurfaceControl.Transaction t = mTransactionPool.acquire();
- t.setPosition(mImeSourceControl.getLeash(), x, value);
+ t.setPosition(animatingLeash, x, value);
if (DEBUG) {
Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+ imeTop(hiddenY) + "->" + imeTop(shownY)
@@ -573,19 +569,19 @@
final float alpha = (mAnimateAlpha || isFloating)
? (value - hiddenY) / (shownY - hiddenY)
: 1.f;
- t.setAlpha(mImeSourceControl.getLeash(), alpha);
+ t.setAlpha(animatingLeash, alpha);
if (mAnimationDirection == DIRECTION_SHOW) {
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
- t.show(mImeSourceControl.getLeash());
+ t.show(animatingLeash);
}
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId, mAnimationDirection, alpha, value, endY,
- Objects.toString(mImeSourceControl.getLeash()),
- Objects.toString(mImeSourceControl.getInsetsHint()),
- Objects.toString(mImeSourceControl.getSurfacePosition()),
+ Objects.toString(animatingLeash),
+ Objects.toString(animatingControl.getInsetsHint()),
+ Objects.toString(animatingControl.getSurfacePosition()),
Objects.toString(mImeFrame));
}
t.apply();
@@ -599,31 +595,23 @@
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId,
- Objects.toString(mImeSourceControl.getInsetsHint()));
+ Objects.toString(animatingControl.getInsetsHint()));
}
}
@Override
public void onAnimationEnd(Animator animation) {
- boolean hasLeash =
- mImeSourceControl != null && mImeSourceControl.getLeash() != null;
if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
SurfaceControl.Transaction t = mTransactionPool.acquire();
if (!mCancelled) {
- if (!android.view.inputmethod.Flags.refactorInsetsController()
- || hasLeash) {
- t.setPosition(mImeSourceControl.getLeash(), x, endY);
- t.setAlpha(mImeSourceControl.getLeash(), 1.f);
- }
+ t.setPosition(animatingLeash, x, endY);
+ t.setAlpha(animatingLeash, 1.f);
}
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
- if (!android.view.inputmethod.Flags.refactorInsetsController()
- || hasLeash) {
- t.hide(mImeSourceControl.getLeash());
- }
+ t.hide(animatingLeash);
removeImeSurface(mDisplayId);
ImeTracker.forLogging().onHidden(mStatsToken);
} else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) {
@@ -636,13 +624,9 @@
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId, mAnimationDirection, endY,
- Objects.toString(
- mImeSourceControl != null ? mImeSourceControl.getLeash()
- : "null"),
- Objects.toString(mImeSourceControl != null
- ? mImeSourceControl.getInsetsHint() : "null"),
- Objects.toString(mImeSourceControl != null
- ? mImeSourceControl.getSurfacePosition() : "null"),
+ Objects.toString(animatingLeash),
+ Objects.toString(animatingControl.getInsetsHint()),
+ Objects.toString(animatingControl.getSurfacePosition()),
Objects.toString(mImeFrame));
}
t.apply();
@@ -650,6 +634,7 @@
mAnimationDirection = DIRECTION_NONE;
mAnimation = null;
+ animatingControl.release(SurfaceControl::release);
}
});
if (!show) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index fad3dee..1929729 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -42,6 +42,7 @@
.setSourceCrop(crop)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
+ .setHintForSeamlessTransition(true)
.build()));
}
@@ -78,6 +79,9 @@
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
mTransaction.reparent(mScreenshot, mParentSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
+ if (buffer.containsHdrLayers()) {
+ mTransaction.setDimmingEnabled(mScreenshot, false);
+ }
mTransaction.show(mScreenshot);
mTransaction.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
index 4b138e4..dd17e29 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
@@ -17,7 +17,6 @@
package com.android.wm.shell.common;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
/**
* Helpers for handling surface.
@@ -25,16 +24,15 @@
public class SurfaceUtils {
/** Creates a dim layer above host surface. */
public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host,
- String name, SurfaceSession surfaceSession) {
- final SurfaceControl dimLayer = makeColorLayer(host, name, surfaceSession);
+ String name) {
+ final SurfaceControl dimLayer = makeColorLayer(host, name);
t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
return dimLayer;
}
/** Creates a color layer for host surface. */
- public static SurfaceControl makeColorLayer(SurfaceControl host, String name,
- SurfaceSession surfaceSession) {
- return new SurfaceControl.Builder(surfaceSession)
+ public static SurfaceControl makeColorLayer(SurfaceControl host, String name) {
+ return new SurfaceControl.Builder()
.setParent(host)
.setColorLayer()
.setName(name)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index ef33b38..3dc86de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -42,7 +42,6 @@
import android.view.ScrollCaptureResponse;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -311,7 +310,7 @@
@Override
protected SurfaceControl getParentSurface(IWindow window,
WindowManager.LayoutParams attrs) {
- SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession())
+ SurfaceControl leash = new SurfaceControl.Builder()
.setContainerLayer()
.setName("SystemWindowLeash")
.setHidden(false)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index dcf84d9..7070ce9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -24,7 +24,6 @@
import android.content.pm.PackageManager
import android.graphics.Rect
import android.os.RemoteException
-import android.os.SystemProperties
import android.util.DisplayMetrics
import android.util.Log
import android.util.Pair
@@ -178,9 +177,7 @@
"org.chromium.arc", 0)
val isTv = AppGlobals.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK, 0)
- isPip2ExperimentEnabled = SystemProperties.getBoolean(
- "persist.wm_shell.pip2", false) ||
- (Flags.enablePip2Implementation() && !isArc && !isTv)
+ isPip2ExperimentEnabled = Flags.enablePip2() && !isArc && !isTv
}
return isPip2ExperimentEnabled as Boolean
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 8156a9c..f7f45ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -235,7 +235,7 @@
private SnapTarget snap(int position, boolean hardDismiss) {
if (shouldApplyFreeSnapMode(position)) {
- return new SnapTarget(position, position, SNAP_TO_NONE);
+ return new SnapTarget(position, SNAP_TO_NONE);
}
int minIndex = -1;
float minDistance = Float.MAX_VALUE;
@@ -263,7 +263,7 @@
if (dockedSide == DOCKED_RIGHT) {
startPos += mInsets.left;
}
- mTargets.add(new SnapTarget(startPos, startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
+ mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
switch (mSnapMode) {
case SNAP_MODE_16_9:
addRatio16_9Targets(isHorizontalDivision, dividerMax);
@@ -278,7 +278,7 @@
addMinimizedTarget(isHorizontalDivision, dockedSide);
break;
}
- mTargets.add(new SnapTarget(dividerMax, dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
+ mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
}
private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
@@ -325,14 +325,14 @@
*/
private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) {
if (smallerSize >= mMinimalSizeResizableTask) {
- mTargets.add(new SnapTarget(position, position, snapPosition));
+ mTargets.add(new SnapTarget(position, snapPosition));
}
}
private void addMiddleTarget(boolean isHorizontalDivision) {
int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
mInsets, mDisplayWidth, mDisplayHeight, mDividerSize);
- mTargets.add(new SnapTarget(position, position, SNAP_TO_50_50));
+ mTargets.add(new SnapTarget(position, SNAP_TO_50_50));
}
private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) {
@@ -346,7 +346,7 @@
position = mDisplayWidth - position - mInsets.right - mDividerSize;
}
}
- mTargets.add(new SnapTarget(position, position, SNAP_TO_MINIMIZE));
+ mTargets.add(new SnapTarget(position, SNAP_TO_MINIMIZE));
}
public SnapTarget getMiddleTarget() {
@@ -377,20 +377,15 @@
}
/**
- * Represents a snap target for the divider.
+ * An object, calculated at boot time, representing a legal position for the split screen
+ * divider (i.e. the divider can be dragged to this spot).
*/
public static class SnapTarget {
/** Position of this snap target. The right/bottom edge of the top/left task snaps here. */
public final int position;
/**
- * Like {@link #position}, but used to calculate the task bounds which might be different
- * from the stack bounds.
- */
- public final int taskPosition;
-
- /**
- * An int describing the placement of the divider in this snap target.
+ * An int (enum) describing the placement of the divider in this snap target.
*/
public final @SnapPosition int snapPosition;
@@ -402,14 +397,13 @@
*/
private final float distanceMultiplier;
- public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition) {
- this(position, taskPosition, snapPosition, 1f);
+ public SnapTarget(int position, @SnapPosition int snapPosition) {
+ this(position, snapPosition, 1f);
}
- public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition,
+ public SnapTarget(int position, @SnapPosition int snapPosition,
float distanceMultiplier) {
this.position = position;
- this.taskPosition = taskPosition;
this.snapPosition = snapPosition;
this.distanceMultiplier = distanceMultiplier;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 7175e36..de3152a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -43,7 +43,6 @@
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -74,7 +73,6 @@
private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground";
private final IconProvider mIconProvider;
- private final SurfaceSession mSurfaceSession;
private Drawable mIcon;
private ImageView mVeilIconView;
@@ -103,17 +101,15 @@
private int mOffsetY;
private int mRunningAnimationCount = 0;
- public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
- SurfaceSession surfaceSession) {
+ public SplitDecorManager(Configuration configuration, IconProvider iconProvider) {
super(configuration, null /* rootSurface */, null /* hostInputToken */);
mIconProvider = iconProvider;
- mSurfaceSession = surfaceSession;
}
@Override
protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) {
// Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder()
.setContainerLayer()
.setName(TAG)
.setHidden(true)
@@ -238,7 +234,7 @@
if (mBackgroundLeash == null) {
mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
- RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ RESIZING_BACKGROUND_SURFACE_NAME);
t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
.setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1);
}
@@ -248,7 +244,7 @@
final int left = isLandscape ? mOldMainBounds.width() : 0;
final int top = isLandscape ? 0 : mOldMainBounds.height();
mGapBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
- GAP_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ GAP_BACKGROUND_SURFACE_NAME);
// Fill up another side bounds area.
t.setColor(mGapBackgroundLeash, getResizingBackgroundColor(resizingTask))
.setLayer(mGapBackgroundLeash, Integer.MAX_VALUE - 2)
@@ -405,7 +401,7 @@
if (mBackgroundLeash == null) {
// Initialize background
mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
- RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ RESIZING_BACKGROUND_SURFACE_NAME);
t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
.setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 2a934cb..b8aa1b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -72,11 +72,12 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.StageTaskListener;
import java.io.PrintWriter;
@@ -543,7 +544,7 @@
* to middle position if the provided SnapTarget is not supported.
*/
public void setDivideRatio(@PersistentSnapPosition int snapPosition) {
- final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget(
+ final SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget(
snapPosition);
setDividerPosition(snapTarget != null
@@ -577,7 +578,7 @@
* Sets new divider position and updates bounds correspondingly. Notifies listener if the new
* target indicates dismissing split.
*/
- public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
+ public void snapToTarget(int currentPosition, SnapTarget snapTarget) {
switch (snapTarget.snapPosition) {
case SNAP_TO_START_AND_DISMISS:
flingDividerPosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
@@ -613,10 +614,10 @@
}
/**
- * Returns {@link DividerSnapAlgorithm.SnapTarget} which matches passing position and velocity.
+ * Returns {@link SnapTarget} which matches passing position and velocity.
* If hardDismiss is set to {@code true}, it will be harder to reach dismiss target.
*/
- public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity,
+ public SnapTarget findSnapTarget(int position, float velocity,
boolean hardDismiss) {
return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 46c1a43..c5f1974 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -36,7 +36,6 @@
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -98,7 +97,7 @@
@Override
protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) {
// Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder()
.setContainerLayer()
.setName(TAG)
.setHidden(true)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 972b78f..6146ecd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -831,7 +831,6 @@
*/
static class CompatUIHintsState {
boolean mHasShownSizeCompatHint;
- boolean mHasShownCameraCompatHint;
boolean mHasShownUserAspectRatioSettingsButtonHint;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
index 0564c95..d2b4f1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -38,7 +38,6 @@
import android.view.IWindow;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -173,7 +172,7 @@
@Override
protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) {
String className = getClass().getSimpleName();
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder()
.setContainerLayer()
.setName(className + "Leash")
.setHidden(false)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt
index 831b331..abc26cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt
@@ -24,7 +24,6 @@
import android.view.IWindow
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
-import android.view.SurfaceSession
import android.view.View
import android.view.WindowManager
import android.view.WindowlessWindowManager
@@ -106,7 +105,7 @@
attrs: WindowManager.LayoutParams
): SurfaceControl? {
val className = javaClass.simpleName
- val builder = SurfaceControl.Builder(SurfaceSession())
+ val builder = SurfaceControl.Builder()
.setContainerLayer()
.setName(className + "Leash")
.setHidden(false)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 42937c1..4adea23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -148,7 +148,11 @@
* dependencies that are device/form factor SystemUI implementation specific should go into their
* respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.)
*/
-@Module(includes = WMShellConcurrencyModule.class)
+@Module(
+ includes = {
+ WMShellConcurrencyModule.class,
+ WMShellCoroutinesModule.class
+ })
public abstract class WMShellBaseModule {
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 46cb6ec..7054c17c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -38,6 +38,7 @@
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -73,6 +74,7 @@
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator;
import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
import com.android.wm.shell.desktopmode.education.AppHandleEducationFilter;
import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
import com.android.wm.shell.draganddrop.DragAndDropController;
@@ -118,6 +120,8 @@
import dagger.Module;
import dagger.Provides;
+import kotlinx.coroutines.CoroutineScope;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -237,6 +241,7 @@
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) {
@@ -260,6 +265,7 @@
rootTaskDisplayAreaOrganizer,
interactionJankMonitor,
genericLinksParser,
+ assistContentRequester,
multiInstanceHelper,
desktopTasksLimiter,
desktopActivityOrientationHandler);
@@ -288,6 +294,15 @@
return new AppToWebGenericLinksParser(context, mainExecutor);
}
+ @Provides
+ static AssistContentRequester provideAssistContentRequester(
+ Context context,
+ @ShellMainThread ShellExecutor shellExecutor,
+ @ShellBackgroundThread ShellExecutor bgExecutor
+ ) {
+ return new AssistContentRequester(context, shellExecutor, bgExecutor);
+ }
+
//
// Freeform
//
@@ -743,6 +758,17 @@
return new AppHandleEducationFilter(context, appHandleEducationDatastoreRepository);
}
+ @WMSingleton
+ @Provides
+ static AppHandleEducationController provideAppHandleEducationController(
+ AppHandleEducationFilter appHandleEducationFilter,
+ ShellTaskOrganizer shellTaskOrganizer,
+ AppHandleEducationDatastoreRepository appHandleEducationDatastoreRepository,
+ @ShellMainThread CoroutineScope applicationScope) {
+ return new AppHandleEducationController(appHandleEducationFilter,
+ shellTaskOrganizer, appHandleEducationDatastoreRepository, applicationScope);
+ }
+
//
// Drag and drop
//
@@ -784,7 +810,8 @@
@Provides
static Object provideIndependentShellComponentsToCreate(
DragAndDropController dragAndDropController,
- Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional
+ Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional,
+ AppHandleEducationController appHandleEducationController
) {
return new Object();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index bfc0ee8..7261919 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -45,6 +45,7 @@
import androidx.annotation.VisibleForTesting;
+import com.android.internal.policy.SystemBarUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -173,8 +174,7 @@
final Region region = new Region();
int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM
|| mDragStartState == DragStartState.DRAGGED_INTENT
- ? mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness)
+ ? SystemBarUtils.getStatusBarHeight(mContext)
: 2 * layout.stableInsets().top;
// A Rect at the top of the screen that takes up the center 40%.
if (mDragStartState == DragStartState.FROM_FREEFORM) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 90f8276..1d16980 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -734,17 +734,33 @@
* Quick-resize to the right or left half of the stable bounds.
*
* @param taskInfo current task that is being snap-resized via dragging or maximize menu button
+ * @param taskSurface the leash of the task being dragged
* @param currentDragBounds current position of the task leash being dragged (or current task
* bounds if being snapped resize via maximize menu button)
* @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
*/
fun snapToHalfScreen(
taskInfo: RunningTaskInfo,
+ taskSurface: SurfaceControl,
currentDragBounds: Rect,
position: SnapPosition
) {
val destinationBounds = getSnapBounds(taskInfo, position)
- if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
+ if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) {
+ // Handle the case where we attempt to snap resize when already snap resized: the task
+ // position won't need to change but we want to animate the surface going back to the
+ // snapped position from the "dragged-to-the-edge" position.
+ if (destinationBounds != currentDragBounds) {
+ returnToDragStartAnimator.start(
+ taskInfo.taskId,
+ taskSurface,
+ startBounds = currentDragBounds,
+ endBounds = destinationBounds,
+ isResizable = taskInfo.isResizeable
+ )
+ }
+ return
+ }
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
@@ -774,13 +790,14 @@
taskInfo.taskId,
taskSurface,
startBounds = currentDragBounds,
- endBounds = dragStartBounds
+ endBounds = dragStartBounds,
+ isResizable = taskInfo.isResizeable,
)
} else {
interactionJankMonitor.begin(
taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"
)
- snapToHalfScreen(taskInfo, currentDragBounds, position)
+ snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position)
}
}
@@ -896,6 +913,7 @@
val intent = Intent(context, DesktopWallpaperActivity::class.java)
val options =
ActivityOptions.makeBasic().apply {
+ launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
index 4c5258f..f4df42c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
@@ -48,7 +48,13 @@
}
/** Builds new animator and starts animation of task leash reposition. */
- fun start(taskId: Int, taskSurface: SurfaceControl, startBounds: Rect, endBounds: Rect) {
+ fun start(
+ taskId: Int,
+ taskSurface: SurfaceControl,
+ startBounds: Rect,
+ endBounds: Rect,
+ isResizable: Boolean
+ ) {
val tx = transactionSupplier.get()
boundsAnimator?.cancel()
@@ -81,11 +87,13 @@
.apply()
taskRepositionAnimationListener.onAnimationEnd(taskId)
boundsAnimator = null
- Toast.makeText(
- context,
- R.string.desktop_mode_non_resizable_snap_text,
- Toast.LENGTH_SHORT
- ).show()
+ if (!isResizable) {
+ Toast.makeText(
+ context,
+ R.string.desktop_mode_non_resizable_snap_text,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
}
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
new file mode 100644
index 0000000..6013e97
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.desktopmode.education
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.SystemProperties
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/**
+ * Controls app handle education end to end.
+ *
+ * Listen to the user trigger for app handle education, calls an api to check if the education
+ * should be shown and calls an api to show education.
+ */
+@OptIn(kotlinx.coroutines.FlowPreview::class)
[email protected]
+class AppHandleEducationController(
+ private val appHandleEducationFilter: AppHandleEducationFilter,
+ shellTaskOrganizer: ShellTaskOrganizer,
+ private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository,
+ @ShellMainThread private val applicationCoroutineScope: CoroutineScope
+) {
+ init {
+ runIfEducationFeatureEnabled {
+ // TODO: b/361038716 - Use app handle state flow instead of focus task change flow
+ val focusTaskChangeFlow = focusTaskChangeFlow(shellTaskOrganizer)
+ applicationCoroutineScope.launch {
+ // Central block handling the app's educational flow end-to-end.
+ // This flow listens to the changes to the result of
+ // [WindowingEducationProto#hasEducationViewedTimestampMillis()] in datastore proto object
+ isEducationViewedFlow()
+ .flatMapLatest { isEducationViewed ->
+ if (isEducationViewed) {
+ // If the education is viewed then return emptyFlow() that completes immediately.
+ // This will help us to not listen to focus task changes after the education has
+ // been viewed already.
+ emptyFlow()
+ } else {
+ // This flow listens for focus task changes, which trigger the app handle education.
+ focusTaskChangeFlow
+ .filter { runningTaskInfo ->
+ runningTaskInfo.topActivityInfo?.packageName?.let {
+ appHandleEducationFilter.shouldShowAppHandleEducation(it)
+ } ?: false && runningTaskInfo.windowingMode != WINDOWING_MODE_FREEFORM
+ }
+ .distinctUntilChanged()
+ }
+ }
+ .debounce(
+ APP_HANDLE_EDUCATION_DELAY) // Wait for few seconds, if the focus task changes.
+ // During the delay then current emission will be cancelled.
+ .flowOn(Dispatchers.IO)
+ .collectLatest {
+ // Fire and forget show education suspend function, manage entire lifecycle of
+ // tooltip in UI class.
+ }
+ }
+ }
+ }
+
+ private inline fun runIfEducationFeatureEnabled(block: () -> Unit) {
+ if (Flags.enableDesktopWindowingAppHandleEducation()) block()
+ }
+
+ private fun isEducationViewedFlow(): Flow<Boolean> =
+ appHandleEducationDatastoreRepository.dataStoreFlow
+ .map { preferences -> preferences.hasEducationViewedTimestampMillis() }
+ .distinctUntilChanged()
+
+ private fun focusTaskChangeFlow(shellTaskOrganizer: ShellTaskOrganizer): Flow<RunningTaskInfo> =
+ callbackFlow {
+ val focusTaskChange = ShellTaskOrganizer.FocusListener { taskInfo -> trySend(taskInfo) }
+ shellTaskOrganizer.addFocusListener(focusTaskChange)
+ awaitClose { shellTaskOrganizer.removeFocusListener(focusTaskChange) }
+ }
+
+ private companion object {
+ val APP_HANDLE_EDUCATION_DELAY: Long
+ get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
index a7fff8a..f420c5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
@@ -25,9 +25,12 @@
import androidx.datastore.dataStoreFile
import com.android.framework.protobuf.InvalidProtocolBufferException
import com.android.internal.annotations.VisibleForTesting
+import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.time.Duration
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
/**
@@ -46,17 +49,26 @@
serializer = WindowingEducationProtoSerializer,
produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) }))
+ /** Provides dataStore.data flow and handles exceptions thrown during collection */
+ val dataStoreFlow: Flow<WindowingEducationProto> =
+ dataStore.data.catch { exception ->
+ // dataStore.data throws an IOException when an error is encountered when reading data
+ if (exception is IOException) {
+ Log.e(
+ TAG,
+ "Error in reading app handle education related data from datastore, data is " +
+ "stored in a file named $APP_HANDLE_EDUCATION_DATASTORE_FILEPATH",
+ exception)
+ } else {
+ throw exception
+ }
+ }
+
/**
* Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the
* DataStore is empty or there's an error reading, it returns the default value of Proto.
*/
- suspend fun windowingEducationProto(): WindowingEducationProto =
- try {
- dataStore.data.first()
- } catch (e: Exception) {
- Log.e(TAG, "Unable to read from datastore")
- WindowingEducationProto.getDefaultInstance()
- }
+ suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first()
/**
* Updates [AppHandleEducation.appUsageStats] and
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
index 0acc7df..faa97ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
@@ -98,9 +98,8 @@
### Exposing shared code for use in Launcher
Launcher doesn't currently build against the Shell library, but needs to have access to some shared
AIDL interfaces and constants. Currently, all AIDL files, and classes under the
-`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that
+`com.android.wm.shell.shared` package are automatically built into the `SystemUISharedLib` that
Launcher uses.
-If the new code doesn't fall into those categories, they can be added explicitly in the Shell's
-[Android.bp](/libs/WindowManager/Shell/Android.bp) file under the
-`wm_shell_util-sources` filegroup.
\ No newline at end of file
+If the new code doesn't fall into those categories, they should be moved to the Shell shared
+package (`com.android.wm.shell.shared`) under the `WindowManager-Shell-shared` library.
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 1ffa541..832e2d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -118,7 +118,7 @@
@Override
public IBinder startMinimizedModeTransition(WindowContainerTransaction wct) {
- final int type = WindowManager.TRANSIT_TO_BACK;
+ final int type = Transitions.TRANSIT_MINIMIZE;
final IBinder token = mTransitions.startTransition(type, wct, this);
mPendingTransitionTokens.add(token);
return token;
@@ -161,7 +161,8 @@
transition, info.getType(), change);
break;
case WindowManager.TRANSIT_TO_BACK:
- transitionHandled |= startMinimizeTransition(transition);
+ transitionHandled |= startMinimizeTransition(
+ transition, info.getType(), change);
break;
case WindowManager.TRANSIT_CLOSE:
if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
@@ -227,8 +228,20 @@
return handled;
}
- private boolean startMinimizeTransition(IBinder transition) {
- return mPendingTransitionTokens.contains(transition);
+ private boolean startMinimizeTransition(
+ IBinder transition,
+ int type,
+ TransitionInfo.Change change) {
+ if (!mPendingTransitionTokens.contains(transition)) {
+ return false;
+ }
+
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (type != Transitions.TRANSIT_MINIMIZE) {
+ return false;
+ }
+ // TODO(b/361524575): Add minimize animations
+ return true;
}
private boolean startCloseTransition(IBinder transition, TransitionInfo.Change change,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
index 71cc8df..422656c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
@@ -38,7 +38,6 @@
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -105,7 +104,7 @@
@Override
protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) {
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder()
.setColorLayer()
.setBufferSize(mDisplayBounds.width(), mDisplayBounds.height())
.setFormat(PixelFormat.RGB_888)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 4df649c..f060158 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -497,13 +497,8 @@
mCurrentValue = value;
}
- boolean shouldApplyCornerRadius() {
- return !isOutPipDirection(mTransitionDirection);
- }
-
boolean shouldApplyShadowRadius() {
- return !isOutPipDirection(mTransitionDirection)
- && !isRemovePipDirection(mTransitionDirection);
+ return !isRemovePipDirection(mTransitionDirection);
}
boolean inScaleTransition() {
@@ -556,7 +551,7 @@
final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
setCurrentValue(alpha);
getSurfaceTransactionHelper().alpha(tx, leash, alpha)
- .round(tx, leash, shouldApplyCornerRadius())
+ .round(tx, leash, true /* applyCornerRadius */)
.shadow(tx, leash, shouldApplyShadowRadius());
if (!handlePipTransaction(leash, tx, destinationBounds, alpha)) {
tx.apply();
@@ -572,7 +567,7 @@
getSurfaceTransactionHelper()
.resetScale(tx, leash, getDestinationBounds())
.crop(tx, leash, getDestinationBounds())
- .round(tx, leash, shouldApplyCornerRadius())
+ .round(tx, leash, true /* applyCornerRadius */)
.shadow(tx, leash, shouldApplyShadowRadius());
tx.show(leash);
tx.apply();
@@ -686,13 +681,11 @@
getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
adjustedSourceRectHint, initialSourceValue, bounds, insets,
isInPipDirection, fraction);
- if (shouldApplyCornerRadius()) {
- final Rect sourceBounds = new Rect(initialContainerRect);
- sourceBounds.inset(insets);
- getSurfaceTransactionHelper()
- .round(tx, leash, sourceBounds, bounds)
- .shadow(tx, leash, shouldApplyShadowRadius());
- }
+ final Rect sourceBounds = new Rect(initialContainerRect);
+ sourceBounds.inset(insets);
+ getSurfaceTransactionHelper()
+ .round(tx, leash, sourceBounds, bounds)
+ .shadow(tx, leash, shouldApplyShadowRadius());
}
if (!handlePipTransaction(leash, tx, bounds, /* alpha= */ 1f)) {
tx.apply();
@@ -741,11 +734,9 @@
.rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds,
insets, degree, x, y, isOutPipDirection,
rotationDelta == ROTATION_270 /* clockwise */);
- if (shouldApplyCornerRadius()) {
- getSurfaceTransactionHelper()
- .round(tx, leash, sourceBounds, bounds)
- .shadow(tx, leash, shouldApplyShadowRadius());
- }
+ getSurfaceTransactionHelper()
+ .round(tx, leash, sourceBounds, bounds)
+ .shadow(tx, leash, shouldApplyShadowRadius());
if (!handlePipTransaction(leash, tx, bounds, 1f /* alpha */)) {
tx.apply();
}
@@ -761,7 +752,7 @@
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
getSurfaceTransactionHelper()
.alpha(tx, leash, 1f)
- .round(tx, leash, shouldApplyCornerRadius())
+ .round(tx, leash, true /* applyCornerRadius */)
.shadow(tx, leash, shouldApplyShadowRadius());
tx.show(leash);
tx.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e4cd10f..ab222c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -99,6 +99,7 @@
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.Optional;
+import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -831,6 +832,7 @@
mPictureInPictureParams.getTitle());
mPipParamsChangedForwarder.notifySubtitleChanged(
mPictureInPictureParams.getSubtitle());
+ logRemoteActions(mPictureInPictureParams);
}
mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
@@ -1112,6 +1114,7 @@
}
applyNewPictureInPictureParams(newParams);
mPictureInPictureParams = newParams;
+ logRemoteActions(mPictureInPictureParams);
}
@Override
@@ -1420,6 +1423,16 @@
}
}
+ private void logRemoteActions(@NonNull PictureInPictureParams params) {
+ StringJoiner sj = new StringJoiner("|", "[", "]");
+ if (params.hasSetActions()) {
+ params.getActions().forEach((action) -> sj.add(action.getTitle()));
+ }
+
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: PIP remote actions=%s", TAG, sj.toString());
+ }
+
/**
* Animates resizing of the pinned stack given the duration.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 05d1984..2138acc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1102,6 +1102,8 @@
return;
}
+ // NOTE(b/365300020): Legacy enter PiP path, clear the swipe state.
+ mPipTransitionState.setInSwipePipToHomeTransition(false);
final int enterAnimationType = mEnterAnimationType;
if (enterAnimationType == ANIM_TYPE_ALPHA) {
startTransaction.setAlpha(leash, 0f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 0d2b8e7..06d2311 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -35,9 +35,9 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
index 8a9302b..8ebdc96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
@@ -22,6 +22,7 @@
import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Rect;
+import android.view.Surface;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
@@ -51,8 +52,10 @@
@NonNull private final SurfaceControl mLeash;
private final SurfaceControl.Transaction mStartTransaction;
- private final int mEnterAnimationDuration;
+ private final SurfaceControl.Transaction mFinishTransaction;
+ private final int mEnterExitAnimationDuration;
private final @BOUNDS int mDirection;
+ private final @Surface.Rotation int mRotation;
// optional callbacks for tracking animation start and end
@Nullable private Runnable mAnimationStartCallback;
@@ -62,37 +65,59 @@
private final Rect mStartBounds = new Rect();
private final Rect mEndBounds = new Rect();
+ @Nullable private final Rect mSourceRectHint;
+ private final Rect mSourceRectHintInsets = new Rect();
+ private final Rect mZeroInsets = new Rect(0, 0, 0, 0);
+
// Bounds updated by the evaluator as animator is running.
private final Rect mAnimatedRect = new Rect();
private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
private final RectEvaluator mRectEvaluator;
+ private final RectEvaluator mInsetEvaluator;
private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
public PipEnterExitAnimator(Context context,
@NonNull SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
@NonNull Rect baseBounds,
@NonNull Rect startBounds,
@NonNull Rect endBounds,
- @BOUNDS int direction) {
+ @Nullable Rect sourceRectHint,
+ @BOUNDS int direction,
+ @Surface.Rotation int rotation) {
mLeash = leash;
mStartTransaction = startTransaction;
+ mFinishTransaction = finishTransaction;
mBaseBounds.set(baseBounds);
mStartBounds.set(startBounds);
mAnimatedRect.set(startBounds);
mEndBounds.set(endBounds);
mRectEvaluator = new RectEvaluator(mAnimatedRect);
+ mInsetEvaluator = new RectEvaluator(new Rect());
mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
mDirection = direction;
+ mRotation = rotation;
+
+ mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
+ if (mSourceRectHint != null) {
+ mSourceRectHintInsets.set(
+ mSourceRectHint.left - mBaseBounds.left,
+ mSourceRectHint.top - mBaseBounds.top,
+ mBaseBounds.right - mSourceRectHint.right,
+ mBaseBounds.bottom - mSourceRectHint.bottom
+ );
+ }
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
- mEnterAnimationDuration = context.getResources()
+ mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
- setDuration(mEnterAnimationDuration);
+ setObjectValues(startBounds, endBounds);
+ setDuration(mEnterExitAnimationDuration);
setEvaluator(mRectEvaluator);
addListener(this);
addUpdateListener(this);
@@ -118,6 +143,14 @@
@Override
public void onAnimationEnd(@NonNull Animator animation) {
+ if (mFinishTransaction != null) {
+ // finishTransaction might override some state (eg. corner radii) so we want to
+ // manually set the state to the end of the animation
+ mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint,
+ mBaseBounds, mAnimatedRect, getInsets(1f), isInPipDirection(), 1f)
+ .round(mFinishTransaction, mLeash, isInPipDirection())
+ .shadow(mFinishTransaction, mLeash, isInPipDirection());
+ }
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
}
@@ -127,19 +160,32 @@
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
final float fraction = getAnimatedFraction();
+ Rect insets = getInsets(fraction);
+
// TODO (b/350801661): implement fixed rotation
- mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, null,
- mBaseBounds, mAnimatedRect, null, isInPipDirection(), fraction)
+ mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
+ mBaseBounds, mAnimatedRect, insets, isInPipDirection(), fraction)
.round(tx, mLeash, isInPipDirection())
.shadow(tx, mLeash, isInPipDirection());
tx.apply();
}
+ private Rect getInsets(float fraction) {
+ Rect startInsets = isInPipDirection() ? mZeroInsets : mSourceRectHintInsets;
+ Rect endInsets = isInPipDirection() ? mSourceRectHintInsets : mZeroInsets;
+
+ return mInsetEvaluator.evaluate(fraction, startInsets, endInsets);
+ }
+
private boolean isInPipDirection() {
return mDirection == BOUNDS_ENTER;
}
+ private boolean isOutPipDirection() {
+ return mDirection == BOUNDS_EXIT;
+ }
+
// no-ops
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
index e04178e..b3070f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
@@ -35,9 +35,9 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 7f16880..262c14d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.view.SurfaceControl;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.util.Preconditions;
@@ -88,6 +89,11 @@
: new PictureInPictureParams.Builder().build());
}
+ @NonNull
+ public PictureInPictureParams getPictureInPictureParams() {
+ return mPictureInPictureParams;
+ }
+
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
PictureInPictureParams params = taskInfo.pictureInPictureParams;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 44baabd..f93233e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -36,6 +36,7 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -398,17 +399,22 @@
SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");
+ Rect sourceRectHint = null;
+ if (pipChange.getTaskInfo() != null
+ && pipChange.getTaskInfo().pictureInPictureParams != null) {
+ sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+ }
+
PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
- startTransaction, startBounds, startBounds, endBounds,
- PipEnterExitAnimator.BOUNDS_ENTER);
+ startTransaction, finishTransaction, startBounds, startBounds, endBounds,
+ sourceRectHint, PipEnterExitAnimator.BOUNDS_ENTER, Surface.ROTATION_0);
tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
this::onClientDrawAtTransitionEnd);
finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
- animator.setAnimationEndCallback(() -> {
- finishCallback.onTransitionFinished(finishWct.isEmpty() ? null : finishWct);
- });
+ animator.setAnimationEndCallback(() ->
+ finishCallback.onTransitionFinished(finishWct));
animator.start();
return true;
@@ -452,19 +458,53 @@
TransitionInfo.Change pipChange = getChangeByToken(info, pipToken);
if (pipChange == null) {
- return false;
+ // pipChange is null, check to see if we've reparented the PIP activity for
+ // the multi activity case. If so we should use the activity leash instead
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() == null
+ && change.getLastParent() != null
+ && change.getLastParent().equals(pipToken)) {
+ pipChange = change;
+ break;
+ }
+ }
+
+ // failsafe
+ if (pipChange == null) {
+ return false;
+ }
+ }
+
+ // for multi activity, we need to manually set the leash layer
+ if (pipChange.getTaskInfo() == null) {
+ TransitionInfo.Change parent = getChangeByToken(info, pipChange.getParent());
+ if (parent != null) {
+ startTransaction.setLayer(parent.getLeash(), Integer.MAX_VALUE - 1);
+ }
}
Rect startBounds = pipChange.getStartAbsBounds();
Rect endBounds = pipChange.getEndAbsBounds();
SurfaceControl pipLeash = pipChange.getLeash();
+ Preconditions.checkNotNull(pipLeash, "Leash is null for exit transition.");
+
+ Rect sourceRectHint = null;
+ if (pipChange.getTaskInfo() != null
+ && pipChange.getTaskInfo().pictureInPictureParams != null) {
+ // single activity
+ sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+ } else if (mPipTaskListener.getPictureInPictureParams().hasSourceBoundsHint()) {
+ // multi activity
+ sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
+ }
PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
- startTransaction, startBounds, startBounds, endBounds,
- PipEnterExitAnimator.BOUNDS_EXIT);
+ startTransaction, finishTransaction, endBounds, startBounds, endBounds,
+ sourceRectHint, PipEnterExitAnimator.BOUNDS_EXIT, Surface.ROTATION_0);
+
animator.setAnimationEndCallback(() -> {
- finishCallback.onTransitionFinished(null);
mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
+ finishCallback.onTransitionFinished(null);
});
animator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
index ebfd357..799028a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
@@ -21,8 +21,8 @@
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
-import android.view.IRecentsAnimationRunner;
+import com.android.wm.shell.recents.IRecentsAnimationRunner;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.shared.GroupedRecentTaskInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl
new file mode 100644
index 0000000..964e5fd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.recents;
+
+import android.graphics.GraphicBuffer;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.window.PictureInPictureSurfaceTransaction;
+import android.window.TaskSnapshot;
+import android.window.WindowAnimationState;
+
+import com.android.internal.os.IResultReceiver;
+
+/**
+ * Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the
+ * runner control certain aspects of the recents animation, and to notify window manager when the
+ * animation has completed.
+ *
+ * {@hide}
+ */
+interface IRecentsAnimationController {
+
+ /**
+ * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the
+ * current set of task ids provided to the handler.
+ */
+ TaskSnapshot screenshotTask(int taskId);
+
+ /**
+ * Sets the final surface transaction on a Task. This is used by Launcher to notify the system
+ * that animating Activity to PiP has completed and the associated task surface should be
+ * updated accordingly. This should be called before `finish`
+ * @param taskId for which the leash should be updated
+ * @param finishTransaction leash operations for the final transform.
+ * @param overlay the surface control for an overlay being shown above the pip (can be null)
+ */
+ void setFinishTaskTransaction(int taskId,
+ in PictureInPictureSurfaceTransaction finishTransaction, in SurfaceControl overlay);
+
+ /**
+ * Notifies to the system that the animation into Recents should end, and all leashes associated
+ * with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then
+ * the home activity should be moved to the top. Otherwise, the home activity is hidden and the
+ * user is returned to the app.
+ * @param sendUserLeaveHint If set to true, {@link Activity#onUserLeaving} will be sent to the
+ * top resumed app, false otherwise.
+ */
+ void finish(boolean moveHomeToTop, boolean sendUserLeaveHint, in IResultReceiver finishCb);
+
+ /**
+ * Called by the handler to indicate that the recents animation input consumer should be
+ * enabled. This is currently used to work around an issue where registering an input consumer
+ * mid-animation causes the existing motion event chain to be canceled. Instead, the caller
+ * may register the recents animation input consumer prior to starting the recents animation
+ * and then enable it mid-animation to start receiving touch events.
+ */
+ void setInputConsumerEnabled(boolean enabled);
+
+ /**
+ * Sets a state for controller to decide which surface is the destination when the recents
+ * animation is cancelled through fail safe mechanism.
+ */
+ void setWillFinishToHome(boolean willFinishToHome);
+
+ /**
+ * Detach navigation bar from app.
+ *
+ * The system reparents the leash of navigation bar to the app when the recents animation starts
+ * and Launcher should call this method to let system restore the navigation bar to its
+ * original position when the quick switch gesture is finished and will run the fade-in
+ * animation If {@param moveHomeToTop} is {@code true}. Otherwise, restore the navigtation bar
+ * without animation.
+ *
+ * @param moveHomeToTop if {@code true}, the home activity should be moved to the top.
+ * Otherwise, the home activity is hidden and the user is returned to the
+ * app.
+ */
+ void detachNavigationBarFromApp(boolean moveHomeToTop);
+
+ /**
+ * Hand off the ongoing animation of a set of remote targets, to be run by another handler using
+ * the given starting parameters.
+ *
+ * Once the handoff is complete, operations on the old leashes for the given targets as well as
+ * callbacks will become no-ops.
+ *
+ * The number of targets MUST match the number of states, and each state MUST match the target
+ * at the same index.
+ */
+ oneway void handOffAnimation(in RemoteAnimationTarget[] targets,
+ in WindowAnimationState[] states);
+}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
similarity index 93%
rename from core/java/android/view/IRecentsAnimationRunner.aidl
rename to libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
index 37663d5..32c79a2 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package android.view;
+package com.android.wm.shell.recents;
-import android.app.ActivityManager;
import android.graphics.Rect;
import android.view.RemoteAnimationTarget;
-import android.view.IRecentsAnimationController;
import android.window.TaskSnapshot;
import android.os.Bundle;
+import com.android.wm.shell.recents.IRecentsAnimationController;
+
/**
* Interface that is used to callback from window manager to the process that runs a recents
* animation to start or cancel it.
@@ -55,7 +55,6 @@
* @param minimizedHomeBounds Specifies the bounds of the minimized home app, will be
* {@code null} if the device is not currently in split screen
*/
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void onAnimationStart(in IRecentsAnimationController controller,
in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
in Rect homeContentInsets, in Rect minimizedHomeBounds, in Bundle extras) = 2;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 39bea1b..a6e25a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -21,9 +21,12 @@
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
+import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -34,7 +37,6 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.view.IRecentsAnimationRunner;
import android.window.WindowContainerToken;
import androidx.annotation.BinderThread;
@@ -51,6 +53,7 @@
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.recents.IRecentsAnimationRunner;
import com.android.wm.shell.shared.GroupedRecentTaskInfo;
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
@@ -158,6 +161,7 @@
return new IRecentTasksImpl(this);
}
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
private void onInit() {
mShellController.addExternalInterface(KEY_EXTRA_SHELL_RECENT_TASKS,
this::createExternalInterface, this);
@@ -168,6 +172,8 @@
mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
mMainExecutor);
}
+ mContext.getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
+ mMainExecutor, isKeyguardLocked -> notifyRecentTasksChanged());
}
void setTransitionHandler(RecentsTransitionHandler handler) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index c90da05..c660000 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -49,8 +49,6 @@
import android.util.Pair;
import android.util.Slog;
import android.view.Display;
-import android.view.IRecentsAnimationController;
-import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.PictureInPictureSurfaceTransaction;
@@ -1024,10 +1022,6 @@
}
@Override
- public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) {
- }
-
- @Override
public void setFinishTaskTransaction(int taskId,
PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
mExecutor.execute(() -> {
@@ -1254,14 +1248,6 @@
}
@Override
- public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
- }
-
- @Override
- public void cleanupScreenshot() {
- }
-
- @Override
public void setWillFinishToHome(boolean willFinishToHome) {
mExecutor.execute(() -> {
mWillFinishToHome = willFinishToHome;
@@ -1269,14 +1255,6 @@
}
/**
- * @see IRecentsAnimationController#removeTask
- */
- @Override
- public boolean removeTask(int taskId) {
- return false;
- }
-
- /**
* @see IRecentsAnimationController#detachNavigationBarFromApp
*/
@Override
@@ -1292,13 +1270,6 @@
}
});
}
-
- /**
- * @see IRecentsAnimationController#animateNavigationBarToApp(long)
- */
- @Override
- public void animateNavigationBarToApp(long duration) {
- }
};
/** Utility class to track the state of a task as-seen by recents. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
deleted file mode 100644
index 1cbb8bb..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
-
-import android.content.Context;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.protolog.ProtoLog;
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-
-import java.util.Optional;
-
-/**
- * Main stage for split-screen mode. When split-screen is active all standard activity types launch
- * on the main stage, except for task that are explicitly pinned to the {@link SideStage}.
- * @see StageCoordinator
- */
-class MainStage extends StageTaskListener {
- private boolean mIsActive = false;
-
- MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- iconProvider, windowDecorViewModel);
- }
-
- boolean isActive() {
- return mIsActive;
- }
-
- void activate(WindowContainerTransaction wct, boolean includingTopTask) {
- if (mIsActive) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b",
- includingTopTask);
-
- if (includingTopTask) {
- reparentTopTask(wct);
- }
-
- mIsActive = true;
- }
-
- void deactivate(WindowContainerTransaction wct) {
- deactivate(wct, false /* toTop */);
- }
-
- void deactivate(WindowContainerTransaction wct, boolean toTop) {
- if (!mIsActive) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s",
- toTop, mRootTaskInfo);
- mIsActive = false;
-
- if (mRootTaskInfo == null) return;
- final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.reparentTasks(
- rootToken,
- null /* newParent */,
- null /* windowingModes */,
- null /* activityTypes */,
- toTop);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
deleted file mode 100644
index 27fd309..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.protolog.ProtoLog;
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-
-import java.util.Optional;
-
-/**
- * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up
- * here. All other task are launch in the {@link MainStage}.
- *
- * @see StageCoordinator
- */
-class SideStage extends StageTaskListener {
- private static final String TAG = SideStage.class.getSimpleName();
-
- SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- iconProvider, windowDecorViewModel);
- }
-
- boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
- mChildrenTaskInfo.size(), toTop);
- if (mChildrenTaskInfo.size() == 0) return false;
- wct.reparentTasks(
- mRootTaskInfo.token,
- null /* newParent */,
- null /* windowingModes */,
- null /* activityTypes */,
- toTop);
- return true;
- }
-
- boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
- final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
- task != null);
- if (task == null) return false;
- wct.reparent(task.token, newParent, false /* onTop */);
- return true;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index a6233dc9..b36b1f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -44,13 +44,13 @@
int STAGE_TYPE_UNDEFINED = -1;
/**
* The main stage type.
- * @see MainStage
+ * @see StageTaskListener
*/
int STAGE_TYPE_MAIN = 0;
/**
* The side stage type.
- * @see SideStage
+ * @see StageTaskListener
*/
int STAGE_TYPE_SIDE = 1;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 7e165af..793e2aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -61,7 +61,6 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.WindowManager;
import android.widget.Toast;
import android.window.RemoteTransition;
@@ -897,7 +896,7 @@
private SurfaceControl reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
SurfaceControl.Transaction t, String callsite) {
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder()
.setContainerLayer()
.setName("RecentsAnimationSplitTasks")
.setHidden(false)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 27ded57..2033902 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -24,6 +24,7 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__CHILD_TASK_ENTER_PIP;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_REQUEST;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
@@ -32,6 +33,7 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_DRAG;
@@ -44,6 +46,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_REQUEST;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -57,6 +60,7 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
@@ -133,6 +137,11 @@
@SplitPosition int mainStagePosition, int mainStageUid,
@SplitPosition int sideStagePosition, int sideStageUid,
boolean isLandscape) {
+ if (hasStartedSession()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logEnter: no-op, previous session has not ended");
+ return;
+ }
+
mLoggerSessionId = mIdSequence.newInstanceId();
int enterReason = getLoggerEnterReason(isLandscape);
updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
@@ -140,6 +149,14 @@
updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
sideStageUid);
updateSplitRatioState(splitRatio);
+
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logEnter: enterReason=%d splitRatio=%f "
+ + "mainStagePosition=%d mainStageUid=%d sideStagePosition=%d "
+ + "sideStageUid=%d isLandscape=%b mEnterSessionId=%d mLoggerSessionId=%d",
+ enterReason, splitRatio, mLastMainStagePosition, mLastMainStageUid,
+ mLastSideStagePosition, mLastSideStageUid, isLandscape,
+ mEnterSessionId != null ? mEnterSessionId.getId() : 0, mLoggerSessionId.getId());
+
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER,
enterReason,
@@ -196,6 +213,8 @@
return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
case EXIT_REASON_DESKTOP_MODE:
return SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
+ case EXIT_REASON_FULLSCREEN_REQUEST:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_REQUEST;
case EXIT_REASON_UNKNOWN:
// Fall through
default:
@@ -212,14 +231,25 @@
@SplitPosition int mainStagePosition, int mainStageUid,
@SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
if (mLoggerSessionId == null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logExit: no-op, mLoggerSessionId is null");
// Ignore changes until we've started logging the session
return;
}
if ((mainStagePosition != SPLIT_POSITION_UNDEFINED
&& sideStagePosition != SPLIT_POSITION_UNDEFINED)
|| (mainStageUid != 0 && sideStageUid != 0)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "logExit: no-op, only main or side stage should be set, not both/none");
throw new IllegalArgumentException("Only main or side stage should be set");
}
+
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logExit: exitReason=%d mainStagePosition=%d"
+ + " mainStageUid=%d sideStagePosition=%d sideStageUid=%d isLandscape=%b"
+ + " mLoggerSessionId=%d", getLoggerExitReason(exitReason),
+ getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), mainStageUid,
+ getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), sideStageUid,
+ isLandscape, mLoggerSessionId.getId());
+
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT,
0 /* enterReason */,
@@ -304,25 +334,34 @@
*/
public void logResize(float splitRatio) {
if (mLoggerSessionId == null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: no-op, mLoggerSessionId is null");
// Ignore changes until we've started logging the session
return;
}
if (splitRatio <= 0f || splitRatio >= 1f) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "logResize: no-op, splitRatio indicates that user is dismissing, not resizing");
// Don't bother reporting resizes that end up dismissing the split, that will be logged
// via the exit event
return;
}
if (!updateSplitRatioState(splitRatio)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: no-op, split ratio was not changed");
// Ignore if there are no user perceived changes
return;
}
+
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: splitRatio=%f mLoggerSessionId=%d",
+ mLastSplitRatio, mLoggerSessionId.getId());
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE,
0 /* enterReason */,
0 /* exitReason */,
mLastSplitRatio,
- 0 /* mainStagePosition */, 0 /* mainStageUid */,
- 0 /* sideStagePosition */, 0 /* sideStageUid */,
+ mLastMainStagePosition,
+ mLastMainStageUid,
+ mLastSideStagePosition,
+ mLastSideStageUid,
0 /* dragInstanceId */,
mLoggerSessionId.getId());
}
@@ -333,6 +372,7 @@
public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid,
@SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
if (mLoggerSessionId == null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logSwap: no-op, mLoggerSessionId is null");
// Ignore changes until we've started logging the session
return;
}
@@ -341,6 +381,11 @@
mainStageUid);
updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
sideStageUid);
+
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logSwap: mainStagePosition=%d mainStageUid=%d "
+ + "sideStagePosition=%d sideStageUid=%d mLoggerSessionId=%d",
+ mLastMainStagePosition, mLastMainStageUid, mLastSideStagePosition,
+ mLastSideStageUid, mLoggerSessionId.getId());
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP,
0 /* enterReason */,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8921ceb..1b143eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -34,6 +34,7 @@
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
@@ -103,7 +104,6 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.WindowManager;
import android.widget.Toast;
import android.window.DisplayAreaInfo;
@@ -154,14 +154,12 @@
import java.util.concurrent.Executor;
/**
- * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
- * {@link SideStage} stages.
+ * Coordinates the staging (visibility, sizing, ...) of the split-screen stages.
* Some high-level rules:
- * - The {@link StageCoordinator} is only considered active if the {@link SideStage} contains at
+ * - The {@link StageCoordinator} is only considered active if the other stages contain at
* least one child task.
- * - The {@link MainStage} should only have children if the coordinator is active.
- * - The {@link SplitLayout} divider is only visible if both the {@link MainStage}
- * and {@link SideStage} are visible.
+ * - The {@link SplitLayout} divider is only visible if multiple {@link StageTaskListener}s are
+ * visible
* - Both stages are put under a single-top root task.
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
@@ -172,11 +170,9 @@
private static final String TAG = StageCoordinator.class.getSimpleName();
- private final SurfaceSession mSurfaceSession = new SurfaceSession();
-
- private final MainStage mMainStage;
+ private final StageTaskListener mMainStage;
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
- private final SideStage mSideStage;
+ private final StageTaskListener mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -329,22 +325,20 @@
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
- mMainStage = new MainStage(
+ mMainStage = new StageTaskListener(
mContext,
mTaskOrganizer,
mDisplayId,
mMainStageListener,
mSyncQueue,
- mSurfaceSession,
iconProvider,
mWindowDecorViewModel);
- mSideStage = new SideStage(
+ mSideStage = new StageTaskListener(
mContext,
mTaskOrganizer,
mDisplayId,
mSideStageListener,
mSyncQueue,
- mSurfaceSession,
iconProvider,
mWindowDecorViewModel);
mDisplayController = displayController;
@@ -367,8 +361,9 @@
@VisibleForTesting
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage,
- DisplayController displayController, DisplayImeController displayImeController,
+ ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
+ StageTaskListener sideStage, DisplayController displayController,
+ DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
Handler mainHandler, Optional<RecentTasksController> recentTasks,
@@ -420,10 +415,23 @@
return mSideStageListener.mVisible && mMainStageListener.mVisible;
}
+ private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) {
+ mMainStage.activate(wct, includingTopTask);
+ }
+
public boolean isSplitActive() {
return mMainStage.isActive();
}
+ /**
+ * Deactivates main stage by removing the stage from the top level split root (usually when a
+ * task underneath gets removed from the stage root).
+ * @param reparentToTop whether we want to put the stage root back on top
+ */
+ private void deactivateSplit(WindowContainerTransaction wct, boolean reparentToTop) {
+ mMainStage.deactivate(wct, reparentToTop);
+ }
+
/** @return whether this transition-request has the launch-adjacent flag. */
public boolean requestHasLaunchAdjacentFlag(TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
@@ -496,12 +504,12 @@
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId);
final WindowContainerTransaction wct = new WindowContainerTransaction();
- /**
- * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the
- * {@link SideStage} no longer has children.
- */
+
+ // MainStage will be deactivated in onStageHasChildrenChanged() if the other stages
+ // no longer have children.
+
final boolean result = mSideStage.removeTask(taskId,
- mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null,
+ isSplitActive() ? mMainStage.mRootTaskInfo.token : null,
wct);
mTaskOrganizer.applyTransaction(wct);
return result;
@@ -618,7 +626,7 @@
}
// If split screen is not activated, we're expecting to open a pair of apps to split.
- final int extraTransitType = mMainStage.isActive()
+ final int extraTransitType = isSplitActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);
@@ -661,7 +669,7 @@
}
// If split screen is not activated, we're expecting to open a pair of apps to split.
- final int extraTransitType = mMainStage.isActive()
+ final int extraTransitType = isSplitActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);
@@ -793,10 +801,10 @@
private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
@Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
- if (!mMainStage.isActive()) {
+ if (!isSplitActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- mMainStage.activate(wct, false /* reparent */);
+ activateSplit(wct, false /* reparentToTop */);
}
mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
@@ -860,10 +868,10 @@
return;
}
- if (!mMainStage.isActive()) {
+ if (!isSplitActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- mMainStage.activate(wct, false /* reparent */);
+ activateSplit(wct, false /* reparentToTop */);
}
setSideStagePosition(splitPosition, wct);
@@ -1110,7 +1118,7 @@
*/
void onKeyguardStateChanged(boolean active, boolean occludingTaskRunning) {
mKeyguardActive = active;
- if (!mMainStage.isActive()) {
+ if (!isSplitActive()) {
return;
}
ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
@@ -1154,7 +1162,7 @@
* will do a no-op.
*/
void dismissSplitKeepingLastActiveStage(@ExitReason int reason) {
- if (!mMainStage.isActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) {
+ if (!isSplitActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) {
// no-op
return;
}
@@ -1167,6 +1175,7 @@
mSplitTransitions.startDismissTransition(wct, this, mLastActiveStage, reason);
setSplitsVisible(false);
mBreakOnNextWake = false;
+ logExit(reason);
}
void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
@@ -1177,8 +1186,8 @@
private void exitSplitScreen(@Nullable StageTaskListener childrenToTop,
@ExitReason int exitReason) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b",
- childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive());
- if (!mMainStage.isActive()) return;
+ childrenToTop == mMainStage, exitReasonToString(exitReason), isSplitActive());
+ if (!isSplitActive()) return;
final WindowContainerTransaction wct = new WindowContainerTransaction();
applyExitSplitScreen(childrenToTop, wct, exitReason);
@@ -1188,7 +1197,7 @@
WindowContainerTransaction wct, @ExitReason int exitReason) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s",
exitReasonToString(exitReason));
- if (!mMainStage.isActive() || mIsExiting) return;
+ if (!isSplitActive() || mIsExiting) return;
onSplitScreenExit();
clearSplitPairedInRecents(exitReason);
@@ -1200,7 +1209,7 @@
mSplitLayout.getInvisibleBounds(mTempRect1);
if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
mSideStage.removeAllTasks(wct, false /* toTop */);
- mMainStage.deactivate(wct, false /* toTop */);
+ deactivateSplit(wct, false /* reparentToTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
setRootForceTranslucent(true, wct);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
@@ -1229,7 +1238,7 @@
childrenToTop.fadeOutDecor(() -> {
WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false;
- mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
+ deactivateSplit(finishedWCT, childrenToTop == mMainStage /* reparentToTop */);
mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
setRootForceTranslucent(true, finishedWCT);
@@ -1252,11 +1261,12 @@
}
void dismissSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
- if (!mMainStage.isActive()) return;
+ if (!isSplitActive()) return;
final int stage = getStageOfTask(toTopTaskId);
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(stage, wct);
mSplitTransitions.startDismissTransition(wct, this, stage, exitReason);
+ logExit(exitReason);
}
/**
@@ -1353,6 +1363,7 @@
mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
});
+ logExit(exitReason);
}
/**
@@ -1362,10 +1373,10 @@
*/
void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
- if (!mMainStage.isActive()) return;
+ if (!isSplitActive()) return;
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop);
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
- mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
+ deactivateSplit(wct, stageToTop == STAGE_TYPE_MAIN);
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
@@ -1430,7 +1441,7 @@
setSideStagePosition(startPosition, wct);
mSideStage.addTask(taskInfo, wct);
}
- mMainStage.activate(wct, true /* includingTopTask */);
+ activateSplit(wct, true /* reparentToTop */);
prepareSplitLayout(wct, resizeAnim);
}
@@ -1471,12 +1482,11 @@
mSkipEvictingMainStageChildren = false;
mSplitRequest = null;
updateRecentTasksSplitPair();
- if (!mLogger.hasStartedSession()) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLeftRightSplit());
- }
+
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLeftRightSplit());
}
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
@@ -1572,7 +1582,7 @@
if (stage == STAGE_TYPE_MAIN) {
mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
mSplitLayout.isLeftRightSplit());
- } else {
+ } else if (stage == STAGE_TYPE_SIDE) {
mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLeftRightSplit());
}
@@ -1662,7 +1672,7 @@
mRootTaskInfo = taskInfo;
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
- && mMainStage.isActive()) {
+ && isSplitActive()) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating",
taskInfo.taskId);
// Clear the divider remote animating flag as the divider will be re-rendered to apply
@@ -1916,7 +1926,7 @@
stageListener == mMainStageListener);
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
- if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
+ if (!hasChildren && !mIsExiting && isSplitActive()) {
if (isSideStage && mMainStageListener.mVisible) {
// Exit to main stage if side stage no longer has children.
mSplitLayout.flingDividerToDismiss(
@@ -1931,7 +1941,7 @@
// Dismiss split screen in the background once any sides of the split become empty.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
}
- } else if (isSideStage && hasChildren && !mMainStage.isActive()) {
+ } else if (isSideStage && hasChildren && !isSplitActive()) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareEnterSplitScreen(wct);
@@ -1952,15 +1962,13 @@
clearRequestIfPresented();
updateRecentTasksSplitPair();
- if (!mLogger.hasStartedSession()) {
- if (!mLogger.hasValidEnterSessionId()) {
- mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE);
- }
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLeftRightSplit());
+ if (!mLogger.hasStartedSession() && !mLogger.hasValidEnterSessionId()) {
+ mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE);
}
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLeftRightSplit());
}
}
@@ -2146,7 +2154,7 @@
private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
- if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) {
+ if (displayId != DEFAULT_DISPLAY || !isSplitActive()) {
return;
}
@@ -2270,6 +2278,7 @@
if (isOpening && inFullscreen) {
// One task is opening into fullscreen mode, remove the corresponding split record.
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
+ logExit(EXIT_REASON_FULLSCREEN_REQUEST);
}
if (isSplitActive()) {
@@ -2397,6 +2406,7 @@
if (triggerTask != null) {
mRecentTasks.ifPresent(
recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
+ logExit(EXIT_REASON_CHILD_TASK_ENTER_PIP);
}
@StageType int topStage = STAGE_TYPE_UNDEFINED;
if (isSplitScreenVisible()) {
@@ -2441,7 +2451,7 @@
// Not entering or exiting, so just do some house-keeping and validation.
// If we're not in split-mode, just abort so something else can handle it.
- if (!mMainStage.isActive()) return false;
+ if (!isSplitActive()) return false;
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId());
mSplitLayout.setFreezeDividerWindow(false);
@@ -2683,7 +2693,7 @@
public void onTransitionAnimationComplete() {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete");
// If still playing, let it finish.
- if (!mMainStage.isActive() && !mIsExiting) {
+ if (!isSplitActive() && !mIsExiting) {
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
mSplitLayout.release();
@@ -2738,7 +2748,10 @@
final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN :
(sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED);
pendingEnter.cancel(
- (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct));
+ (cancelWct, cancelT) -> {
+ prepareExitSplitScreen(dismissTop, cancelWct);
+ logExit(EXIT_REASON_UNKNOWN);
+ });
Log.w(TAG, splitFailureMessage("startPendingEnterAnimation",
"launched 2 tasks in split, but didn't receive "
+ "2 tasks in transition. Possibly one of them failed to launch"));
@@ -3147,7 +3160,7 @@
+ (mSplitLayout != null ? mSplitLayout.isLeftRightSplit() : "null"));
pw.println(innerPrefix + "MainStage");
pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition()));
- pw.println(childPrefix + "isActive=" + mMainStage.isActive());
+ pw.println(childPrefix + "isActive=" + isSplitActive());
mMainStage.dump(pw, childPrefix);
pw.println(innerPrefix + "MainStageListener");
mMainStageListener.dump(pw, childPrefix);
@@ -3267,7 +3280,7 @@
@Override
public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
- if (mMainStage.isActive()) {
+ if (isSplitActive()) {
final boolean isMainStage = mMainStageListener == this;
// If visible, we preserve the app and keep it running. If an app becomes
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 99f3832..d64c0a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -39,7 +39,6 @@
import android.util.SparseArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -72,6 +71,10 @@
public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = StageTaskListener.class.getSimpleName();
+ // No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the
+ // stages should have this be set/being used
+ private boolean mIsActive;
+
/** Callback interface for listening to changes in a split-screen stage. */
public interface StageListenerCallbacks {
void onRootTaskAppeared();
@@ -89,7 +92,6 @@
private final Context mContext;
private final StageListenerCallbacks mCallbacks;
- private final SurfaceSession mSurfaceSession;
private final SyncTransactionQueue mSyncQueue;
private final IconProvider mIconProvider;
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
@@ -104,12 +106,11 @@
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
+ IconProvider iconProvider,
Optional<WindowDecorViewModel> windowDecorViewModel) {
mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
- mSurfaceSession = surfaceSession;
mIconProvider = iconProvider;
mWindowDecorViewModel = windowDecorViewModel;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
@@ -199,12 +200,11 @@
mRootTaskInfo = taskInfo;
mSplitDecorManager = new SplitDecorManager(
mRootTaskInfo.configuration,
- mIconProvider,
- mSurfaceSession);
+ mIconProvider);
mCallbacks.onRootTaskAppeared();
sendStatusChanged();
mSyncQueue.runInSync(t -> mDimLayer =
- SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
+ SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer"));
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
final int taskId = taskInfo.taskId;
mChildrenLeashes.put(taskId, leash);
@@ -475,6 +475,68 @@
});
}
+ // ---------
+ // Previously only used in MainStage
+ boolean isActive() {
+ return mIsActive;
+ }
+
+ void activate(WindowContainerTransaction wct, boolean includingTopTask) {
+ if (mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b",
+ includingTopTask);
+
+ if (includingTopTask) {
+ reparentTopTask(wct);
+ }
+
+ mIsActive = true;
+ }
+
+ void deactivate(WindowContainerTransaction wct) {
+ deactivate(wct, false /* toTop */);
+ }
+
+ void deactivate(WindowContainerTransaction wct, boolean toTop) {
+ if (!mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: toTop=%b rootTaskInfo=%s",
+ toTop, mRootTaskInfo);
+ mIsActive = false;
+
+ if (mRootTaskInfo == null) return;
+ final WindowContainerToken rootToken = mRootTaskInfo.token;
+ wct.reparentTasks(
+ rootToken,
+ null /* newParent */,
+ null /* windowingModes */,
+ null /* activityTypes */,
+ toTop);
+ }
+
+ // --------
+ // Previously only used in SideStage
+ boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
+ mChildrenTaskInfo.size(), toTop);
+ if (mChildrenTaskInfo.size() == 0) return false;
+ wct.reparentTasks(
+ mRootTaskInfo.token,
+ null /* newParent */,
+ null /* windowingModes */,
+ null /* activityTypes */,
+ toTop);
+ return true;
+ }
+
+ boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
+ final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
+ task != null);
+ if (task == null) return false;
+ wct.reparent(task.token, newParent, false /* onTop */);
+ return true;
+ }
+
private void sendStatusChanged() {
mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index b18feefe..81f444b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -352,12 +352,17 @@
/** Extract the window background color from {@code attrs}. */
private static int peekWindowBGColor(Context context, SplashScreenWindowAttrs attrs) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "peekWindowBGColor");
- final Drawable themeBGDrawable;
+ Drawable themeBGDrawable = null;
if (attrs.mWindowBgColor != 0) {
themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor);
} else if (attrs.mWindowBgResId != 0) {
- themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
- } else {
+ try {
+ themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
+ } catch (Resources.NotFoundException e) {
+ Slog.w(TAG, "Unable get drawable from resource", e);
+ }
+ }
+ if (themeBGDrawable == null) {
themeBGDrawable = createDefaultBackgroundDrawable();
Slog.w(TAG, "Window background does not exist, using " + themeBGDrawable);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index fac3592..2e9b53e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -33,7 +33,6 @@
import android.util.SparseArray;
import android.view.IWindow;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.window.SplashScreenView;
@@ -204,7 +203,7 @@
@Override
protected SurfaceControl getParentSurface(IWindow window,
WindowManager.LayoutParams attrs) {
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder()
.setContainerLayer()
.setName("Windowless window")
.setHidden(false)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index a85188a..82c0aaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -118,6 +118,13 @@
mTaskViewTaskController.startShortcutActivity(shortcut, options, launchBounds);
}
+ /**
+ * Moves the current task in taskview out of the view and back to fullscreen.
+ */
+ public void moveToFullscreen() {
+ mTaskViewTaskController.moveToFullscreen();
+ }
+
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
if (mTaskViewTaskController.isUsingShellTransitions()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 9750d3e..e74342e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.taskview;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import android.annotation.NonNull;
@@ -256,6 +257,24 @@
mTaskViewTransitions.startInstantTransition(TRANSIT_CHANGE, wct);
}
+ /**
+ * Moves the current task in TaskView out of the view and back to fullscreen.
+ */
+ public void moveToFullscreen() {
+ if (mTaskToken == null) return;
+ mShellExecutor.execute(() -> {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setWindowingMode(mTaskToken, WINDOWING_MODE_UNDEFINED);
+ wct.setAlwaysOnTop(mTaskToken, false);
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+ mTaskViewTransitions.moveTaskViewToFullscreen(wct, this);
+ if (mListener != null) {
+ // Task is being "removed" from the clients perspective
+ mListener.onTaskRemovalStarted(mTaskInfo.taskId);
+ }
+ });
+ }
+
private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
final Binder launchCookie = new Binder();
mShellExecutor.execute(() -> {
@@ -585,7 +604,6 @@
});
}
mTaskViewBase.onTaskVanished(taskInfo);
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, false);
}
}
@@ -699,6 +717,9 @@
mTaskViewBase.setResizeBgColor(startTransaction, backgroundColor);
}
+ // After the embedded task has appeared, set it to non-trimmable. This is important
+ // to prevent recents from trimming and removing the embedded task.
+ wct.setTaskTrimmableFromRecents(taskInfo.token, false /* isTrimmableFromRecents */);
mTaskViewBase.onTaskAppeared(mTaskInfo, mTaskLeash);
if (mListener != null) {
final int taskId = mTaskInfo.taskId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 15fe7ab..39648f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -236,6 +236,12 @@
startNextTransition();
}
+ void moveTaskViewToFullscreen(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskViewTaskController taskView) {
+ mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */));
+ startNextTransition();
+ }
+
/** Starts a new transition to make the given {@code taskView} visible. */
public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
setTaskViewVisible(taskView, visible, false /* reorder */);
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..ff4b981 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;
@@ -92,7 +92,6 @@
import android.util.ArrayMap;
import android.view.Choreographer;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -134,8 +133,6 @@
private final TransitionAnimation mTransitionAnimation;
private final DevicePolicyManager mDevicePolicyManager;
- private final SurfaceSession mSurfaceSession = new SurfaceSession();
-
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
@@ -473,7 +470,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.
@@ -705,7 +702,7 @@
TransitionInfo.Change change, TransitionInfo info, int animHint,
ArrayList<Animator> animations, Runnable onAnimFinish) {
final int rootIdx = TransitionUtil.rootIndexFor(change, info);
- final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext, mSurfaceSession,
+ final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext,
mTransactionPool, startTransaction, change, info.getRoot(rootIdx).getLeash(),
animHint);
// The rotation animation may consist of 3 animations: fade-out screenshot, fade-in real
@@ -767,12 +764,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 +780,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 +902,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;
}
@@ -918,7 +915,7 @@
}
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+ final WindowThumbnail wt = WindowThumbnail.createAndAttach(
change.getLeash(), thumbnail, transaction);
final Animation a =
mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(bounds);
@@ -943,7 +940,7 @@
@NonNull Runnable finishCallback, TransitionInfo.Change change,
TransitionInfo.AnimationOptions options, float cornerRadius) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+ final WindowThumbnail wt = WindowThumbnail.createAndAttach(
change.getLeash(), options.getThumbnail(), transaction);
final Rect bounds = change.getEndAbsBounds();
final int orientation = mContext.getResources().getConfiguration().orientation;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 9b27e41..c385f9a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -30,6 +30,7 @@
import android.view.SurfaceControl;
import android.window.TransitionInfo;
+import com.android.window.flags.Flags;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -71,9 +72,21 @@
final int mode = change.getMode();
final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
- if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME
- && (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture)) {
- notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode) || isBackGesture);
+ if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+ if (Flags.migratePredictiveBackTransition()) {
+ final boolean gestureToHomeTransition = isBackGesture
+ && TransitionUtil.isClosingType(info.getType());
+ if (gestureToHomeTransition
+ || (!isBackGesture && TransitionUtil.isOpenOrCloseMode(mode))) {
+ notifyHomeVisibilityChanged(gestureToHomeTransition
+ || TransitionUtil.isOpeningType(mode));
+ }
+ } else {
+ if (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture) {
+ notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode)
+ || isBackGesture);
+ }
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 0bf9d36..5802e2c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -38,7 +38,6 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceSession;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.window.ScreenCapture;
@@ -112,7 +111,7 @@
/** Intensity of light/whiteness of the layout after rotation occurs. */
private float mEndLuma;
- ScreenRotationAnimation(Context context, SurfaceSession session, TransactionPool pool,
+ ScreenRotationAnimation(Context context, TransactionPool pool,
Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash, int animHint) {
mContext = context;
mTransactionPool = pool;
@@ -126,7 +125,7 @@
mStartRotation = change.getStartRotation();
mEndRotation = change.getEndRotation();
- mAnimLeash = new SurfaceControl.Builder(session)
+ mAnimLeash = new SurfaceControl.Builder()
.setParent(rootLeash)
.setEffectLayer()
.setCallsite("ShellRotationAnimation")
@@ -153,7 +152,7 @@
return;
}
- mScreenshotLayer = new SurfaceControl.Builder(session)
+ mScreenshotLayer = new SurfaceControl.Builder()
.setParent(mAnimLeash)
.setBLASTLayer()
.setSecure(screenshotBuffer.containsSecureLayers())
@@ -178,7 +177,7 @@
t.setCrop(mSurfaceControl, new Rect(0, 0, mEndWidth, mEndHeight));
if (!isCustomRotate()) {
- mBackColorSurface = new SurfaceControl.Builder(session)
+ mBackColorSurface = new SurfaceControl.Builder()
.setParent(rootLeash)
.setColorLayer()
.setOpaque(true)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 7dc336b..aba8b61 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -196,6 +196,9 @@
/** Transition to set windowing mode after exit pip transition is finished animating. */
public static final int TRANSIT_CLEANUP_PIP_EXIT = WindowManager.TRANSIT_FIRST_CUSTOM + 19;
+ /** Transition type to minimize a task. */
+ public static final int TRANSIT_MINIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 20;
+
/** Transition type for desktop mode transitions. */
public static final int TRANSIT_DESKTOP_MODE_TYPES =
WindowManager.TRANSIT_FIRST_CUSTOM + 100;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
index 2c668ed..341f2bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
@@ -21,7 +21,6 @@
import android.graphics.PixelFormat;
import android.hardware.HardwareBuffer;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
/**
* Represents a surface that is displayed over a transition surface.
@@ -33,10 +32,10 @@
private WindowThumbnail() {}
/** Create a thumbnail surface and attach it over a parent surface. */
- static WindowThumbnail createAndAttach(SurfaceSession surfaceSession, SurfaceControl parent,
+ static WindowThumbnail createAndAttach(SurfaceControl parent,
HardwareBuffer thumbnailHeader, SurfaceControl.Transaction t) {
WindowThumbnail windowThumbnail = new WindowThumbnail();
- windowThumbnail.mSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+ windowThumbnail.mSurfaceControl = new SurfaceControl.Builder()
.setParent(parent)
.setName("WindowThumanil : " + parent.toString())
.setCallsite("WindowThumanil")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/AbstractTaskPositionerDecorator.kt
similarity index 72%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/AbstractTaskPositionerDecorator.kt
index 3c5beeb..6dd5ac6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/AbstractTaskPositionerDecorator.kt
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.windowdecor
-parcelable BubbleBarLocation;
\ No newline at end of file
+/**
+ * Abstract decorator for a [TaskPositioner].
+ */
+abstract class AbstractTaskPositionerDecorator(
+ private val taskPositioner: TaskPositioner
+) : TaskPositioner by taskPositioner
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index ac354593..0f8bd28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -90,6 +90,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -182,6 +183,7 @@
private final Region mExclusionRegion = Region.obtain();
private boolean mInImmersiveMode;
private final String mSysUIPackageName;
+ private final AssistContentRequester mAssistContentRequester;
private final DisplayChangeController.OnDisplayChangingListener mOnDisplayChangingListener;
private final ISystemGestureExclusionListener mGestureExclusionListener =
@@ -197,6 +199,7 @@
});
}
};
+ private final TaskPositionerFactory mTaskPositionerFactory;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -217,6 +220,7 @@
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler
@@ -238,6 +242,7 @@
transitions,
desktopTasksController,
genericLinksParser,
+ assistContentRequester,
multiInstanceHelper,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
@@ -246,7 +251,8 @@
new SparseArray<>(),
interactionJankMonitor,
desktopTasksLimiter,
- activityOrientationChangeHandler);
+ activityOrientationChangeHandler,
+ new TaskPositionerFactory());
}
@VisibleForTesting
@@ -267,6 +273,7 @@
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
@@ -275,7 +282,8 @@
SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId,
InteractionJankMonitor interactionJankMonitor,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
- Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) {
+ Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
+ TaskPositionerFactory taskPositionerFactory) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -304,6 +312,7 @@
mInteractionJankMonitor = interactionJankMonitor;
mDesktopTasksLimiter = desktopTasksLimiter;
mActivityOrientationChangeHandler = activityOrientationChangeHandler;
+ mAssistContentRequester = assistContentRequester;
mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
DesktopModeWindowDecoration decoration;
RunningTaskInfo taskInfo;
@@ -329,6 +338,7 @@
}
}
};
+ mTaskPositionerFactory = taskPositionerFactory;
shellInit.addInitCallback(this::onInit, this);
}
@@ -491,7 +501,9 @@
} else {
mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,
Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable");
- mDesktopTasksController.snapToHalfScreen(decoration.mTaskInfo,
+ mDesktopTasksController.snapToHalfScreen(
+ decoration.mTaskInfo,
+ decoration.mTaskSurface,
decoration.mTaskInfo.configuration.windowConfiguration.getBounds(),
left ? SnapPosition.LEFT : SnapPosition.RIGHT);
}
@@ -624,7 +636,7 @@
} else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
if (!decoration.isHandleMenuActive()) {
moveTaskToFront(decoration.mTaskInfo);
- decoration.createHandleMenu(mSplitScreenController);
+ decoration.createHandleMenu();
}
} else if (id == R.id.maximize_window) {
// TODO(b/346441962): move click detection logic into the decor's
@@ -820,7 +832,10 @@
decoration.mTaskSurface,
e.getRawX(dragPointerIdx),
newTaskBounds);
- updateDragStatus(e.getActionMasked());
+ // Flip mIsDragging only if the bounds actually changed.
+ if (mIsDragging || !newTaskBounds.equals(mOnDragStartInitialBounds)) {
+ updateDragStatus(e.getActionMasked());
+ }
return true;
}
case MotionEvent.ACTION_UP:
@@ -1268,26 +1283,22 @@
mSyncQueue,
mRootTaskDisplayAreaOrganizer,
mGenericLinksParser,
+ mAssistContentRequester,
mMultiInstanceHelper);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
- final DragPositioningCallback dragPositioningCallback;
- if (!DesktopModeStatus.isVeiledResizeEnabled()) {
- dragPositioningCallback = new FluidResizeTaskPositioner(
- mTaskOrganizer, mTransitions, windowDecoration, mDisplayController,
- mDragStartListener, mTransactionFactory);
- windowDecoration.setTaskDragResizer(
- (FluidResizeTaskPositioner) dragPositioningCallback);
- } else {
- dragPositioningCallback = new VeiledResizeTaskPositioner(
- mTaskOrganizer, windowDecoration, mDisplayController,
- mDragStartListener, mTransitions, mInteractionJankMonitor);
- windowDecoration.setTaskDragResizer(
- (VeiledResizeTaskPositioner) dragPositioningCallback);
- }
+ final TaskPositioner taskPositioner = mTaskPositionerFactory.create(
+ mTaskOrganizer,
+ windowDecoration,
+ mDisplayController,
+ mDragStartListener,
+ mTransitions,
+ mInteractionJankMonitor,
+ mTransactionFactory);
+ windowDecoration.setTaskDragResizer(taskPositioner);
final DesktopModeTouchEventListener touchEventListener =
- new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
+ new DesktopModeTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setOnMaximizeOrRestoreClickListener(() -> {
onMaximizeOrRestore(taskInfo.taskId, "maximize_menu");
return Unit.INSTANCE;
@@ -1321,7 +1332,7 @@
windowDecoration.setCaptionListeners(
touchEventListener, touchEventListener, touchEventListener, touchEventListener);
windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
- windowDecoration.setDragPositioningCallback(dragPositioningCallback);
+ windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */);
@@ -1470,6 +1481,25 @@
}
}
}
+
+ @VisibleForTesting
+ static class TaskPositionerFactory {
+ TaskPositioner create(
+ ShellTaskOrganizer taskOrganizer,
+ DesktopModeWindowDecoration windowDecoration,
+ DisplayController displayController,
+ DragPositioningCallbackUtility.DragStartListener dragStartListener,
+ Transitions transitions,
+ InteractionJankMonitor interactionJankMonitor,
+ Supplier<SurfaceControl.Transaction> transactionFactory) {
+ if (!DesktopModeStatus.isVeiledResizeEnabled()) {
+ return new FluidResizeTaskPositioner(
+ taskOrganizer, transitions, windowDecoration, displayController,
+ dragStartListener, transactionFactory);
+ }
+ return new VeiledResizeTaskPositioner(
+ taskOrganizer, windowDecoration, displayController,
+ dragStartListener, transitions, interactionJankMonitor);
+ }
+ }
}
-
-
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 81251b8..142be91 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -38,6 +38,7 @@
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
+import android.app.assist.AssistContent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -75,6 +76,8 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AppToWebUtils;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.MultiInstanceHelper;
@@ -114,6 +117,7 @@
private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue;
private final SplitScreenController mSplitScreenController;
+ private final WindowManagerWrapper mWindowManagerWrapper;
private WindowDecorationViewHolder mWindowDecorViewHolder;
private View.OnClickListener mOnCaptionButtonClickListener;
@@ -149,6 +153,7 @@
private CharSequence mAppName;
private CapturedLink mCapturedLink;
private Uri mGenericLink;
+ private Uri mWebUri;
private Consumer<Uri> mOpenInBrowserClickListener;
private ExclusionRegionListener mExclusionRegionListener;
@@ -157,6 +162,7 @@
private final MaximizeMenuFactory mMaximizeMenuFactory;
private final HandleMenuFactory mHandleMenuFactory;
private final AppToWebGenericLinksParser mGenericLinksParser;
+ private final AssistContentRequester mAssistContentRequester;
// Hover state for the maximize menu and button. The menu will remain open as long as either of
// these is true. See {@link #onMaximizeHoverStateChanged()}.
@@ -183,14 +189,16 @@
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper) {
this (context, userContext, displayController, splitScreenController, taskOrganizer,
taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue,
- rootTaskDisplayAreaOrganizer, genericLinksParser, SurfaceControl.Builder::new,
- SurfaceControl.Transaction::new, WindowContainerTransaction::new,
- SurfaceControl::new, new SurfaceControlViewHostFactory() {},
- DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE,
- multiInstanceHelper);
+ rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
+ context.getSystemService(WindowManager.class)),
+ new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE,
+ DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper);
}
DesktopModeWindowDecoration(
@@ -207,10 +215,12 @@
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
Supplier<SurfaceControl> surfaceControlSupplier,
+ WindowManagerWrapper windowManagerWrapper,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
MaximizeMenuFactory maximizeMenuFactory,
HandleMenuFactory handleMenuFactory,
@@ -226,9 +236,11 @@
mSyncQueue = syncQueue;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mGenericLinksParser = genericLinksParser;
+ mAssistContentRequester = assistContentRequester;
mMaximizeMenuFactory = maximizeMenuFactory;
mHandleMenuFactory = handleMenuFactory;
mMultiInstanceHelper = multiInstanceHelper;
+ mWindowManagerWrapper = windowManagerWrapper;
}
/**
@@ -473,10 +485,18 @@
@Nullable
private Uri getBrowserLink() {
+ // Do not show browser link in browser applications
+ final ComponentName baseActivity = mTaskInfo.baseActivity;
+ if (baseActivity != null && AppToWebUtils.isBrowserApp(mContext,
+ baseActivity.getPackageName(), mUserContext.getUserId())) {
+ return null;
+ }
// If the captured link is available and has not expired, return the captured link.
// Otherwise, return the generic link which is set to null if a generic link is unavailable.
if (mCapturedLink != null && !mCapturedLink.mExpired) {
return mCapturedLink.mUri;
+ } else if (mWebUri != null) {
+ return mWebUri;
}
return mGenericLink;
}
@@ -574,7 +594,8 @@
return new AppHandleViewHolder(
mResult.mRootView,
mOnCaptionTouchListener,
- mOnCaptionButtonClickListener
+ mOnCaptionButtonClickListener,
+ mWindowManagerWrapper
);
} else if (mRelayoutParams.mLayoutResId
== R.layout.desktop_mode_app_header) {
@@ -981,17 +1002,32 @@
}
/**
- * Create and display handle menu window.
+ * Updates app info and creates and displays handle menu window.
*/
- void createHandleMenu(SplitScreenController splitScreenController) {
+ void createHandleMenu() {
+ // Requests assist content. When content is received, calls {@link #onAssistContentReceived}
+ // which sets app info and creates the handle menu.
+ mAssistContentRequester.requestAssistContent(
+ mTaskInfo.taskId, this::onAssistContentReceived);
+ }
+
+ /**
+ * Called when assist content is received. updates the saved links and creates the handle menu.
+ */
+ @VisibleForTesting
+ void onAssistContentReceived(@Nullable AssistContent assistContent) {
+ mWebUri = assistContent == null ? null : assistContent.getWebUri();
loadAppInfoIfNeeded();
updateGenericLink();
+
+ // Create and display handle menu
mHandleMenu = mHandleMenuFactory.create(
this,
+ mWindowManagerWrapper,
mRelayoutParams.mLayoutResId,
mAppIconBitmap,
mAppName,
- splitScreenController,
+ mSplitScreenController,
DesktopModeStatus.canEnterDesktopMode(mContext),
Flags.enableDesktopWindowingMultiInstanceFeatures()
&& mMultiInstanceHelper
@@ -1005,6 +1041,7 @@
mHandleMenu.show(
/* onToDesktopClickListener= */ () -> {
mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
+ mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
return Unit.INSTANCE;
},
/* onToFullscreenClickListener= */ mOnToFullscreenClickListener,
@@ -1326,6 +1363,7 @@
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper) {
return new DesktopModeWindowDecoration(
context,
@@ -1341,6 +1379,7 @@
syncQueue,
rootTaskDisplayAreaOrganizer,
genericLinksParser,
+ assistContentRequester,
multiInstanceHelper);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index e2d42b2..3853f1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -49,8 +49,7 @@
* that we send the final shell transition since we still utilize the {@link #onTransitionConsumed}
* callback.
*/
-class FluidResizeTaskPositioner implements DragPositioningCallback,
- TaskDragResizer, Transitions.TransitionHandler {
+class FluidResizeTaskPositioner implements TaskPositioner, Transitions.TransitionHandler {
private final ShellTaskOrganizer mTaskOrganizer;
private final Transitions mTransitions;
private final WindowDecoration mWindowDecoration;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 34de94e..748046e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -64,6 +64,7 @@
*/
class HandleMenu(
private val parentDecor: DesktopModeWindowDecoration,
+ private val windowManagerWrapper: WindowManagerWrapper,
private val layoutResId: Int,
private val appIconBitmap: Bitmap?,
private val appName: CharSequence?,
@@ -178,7 +179,7 @@
handleMenuViewContainer =
if (!taskInfo.isFreeform && Flags.enableAdditionalWindowsAboveStatusBar()) {
AdditionalSystemViewContainer(
- context = context,
+ windowManagerWrapper = windowManagerWrapper,
taskId = taskInfo.taskId,
x = x,
y = y,
@@ -635,6 +636,7 @@
interface HandleMenuFactory {
fun create(
parentDecor: DesktopModeWindowDecoration,
+ windowManagerWrapper: WindowManagerWrapper,
layoutResId: Int,
appIconBitmap: Bitmap?,
appName: CharSequence?,
@@ -652,6 +654,7 @@
object DefaultHandleMenuFactory : HandleMenuFactory {
override fun create(
parentDecor: DesktopModeWindowDecoration,
+ windowManagerWrapper: WindowManagerWrapper,
layoutResId: Int,
appIconBitmap: Bitmap?,
appName: CharSequence?,
@@ -665,6 +668,7 @@
): HandleMenu {
return HandleMenu(
parentDecor,
+ windowManagerWrapper,
layoutResId,
appIconBitmap,
appName,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
index fd6c4d8..fb81ed4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
@@ -30,7 +30,6 @@
import android.view.LayoutInflater
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
-import android.view.SurfaceSession
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL
import android.view.WindowlessWindowManager
@@ -66,7 +65,6 @@
private val lightColors = dynamicLightColorScheme(context)
private val darkColors = dynamicDarkColorScheme(context)
- private val surfaceSession = SurfaceSession()
private lateinit var iconView: ImageView
private var iconSize = 0
@@ -126,7 +124,7 @@
.setCallsite("ResizeVeil#setupResizeVeil")
.build()
backgroundSurface = surfaceControlBuilderFactory
- .create("Resize veil background of Task=" + taskInfo.taskId, surfaceSession)
+ .create("Resize veil background of Task=" + taskInfo.taskId)
.setColorLayer()
.setHidden(true)
.setParent(veilSurface)
@@ -399,10 +397,6 @@
fun create(name: String): SurfaceControl.Builder {
return SurfaceControl.Builder().setName(name)
}
-
- fun create(name: String, surfaceSession: SurfaceSession): SurfaceControl.Builder {
- return SurfaceControl.Builder(surfaceSession).setName(name)
- }
}
companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
index 40421b5..d7ea0c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
@@ -19,7 +19,7 @@
/**
* Holds the state of a drag resize.
*/
-interface TaskDragResizer {
+public interface TaskDragResizer {
/**
* Returns true if task is currently being resized or animating the final transition after
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.kt
similarity index 79%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.kt
index 3c5beeb..96c43da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.windowdecor
-parcelable BubbleBarLocation;
\ No newline at end of file
+/**
+ * Interface for TaskPositioner.
+ */
+interface TaskPositioner : DragPositioningCallback, TaskDragResizer
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 4a884eb5..5998155 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -47,8 +47,7 @@
* If the drag is resizing the task, we resize the veil instead.
* If the drag is repositioning, we update in the typical manner.
*/
-public class VeiledResizeTaskPositioner implements DragPositioningCallback,
- TaskDragResizer, Transitions.TransitionHandler {
+public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.TransitionHandler {
private DesktopModeWindowDecoration mDesktopWindowDecoration;
private ShellTaskOrganizer mTaskOrganizer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt
new file mode 100644
index 0000000..5c2ff1b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.windowdecor
+
+import android.view.View
+import android.view.WindowManager
+
+/**
+ * A wrapper for [WindowManager] to make view manipulation operations related to window
+ * decors more testable.
+ */
+class WindowManagerWrapper (
+ private val windowManager: WindowManager
+){
+
+ fun addView(v: View, lp: WindowManager.LayoutParams) {
+ windowManager.addView(v, lp)
+ }
+
+ fun removeViewImmediate(v: View) {
+ windowManager.removeViewImmediate(v)
+ }
+
+ fun updateViewLayout(v: View, lp: WindowManager.LayoutParams) {
+ windowManager.updateViewLayout(v, lp)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index cadd80e..226b0fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -24,13 +24,14 @@
import android.view.SurfaceControl
import android.view.View
import android.view.WindowManager
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
/**
* An [AdditionalViewContainer] that uses the system [WindowManager] instance. Intended
* for view containers that should be above the status bar layer.
*/
class AdditionalSystemViewContainer(
- context: Context,
+ private val windowManagerWrapper: WindowManagerWrapper,
taskId: Int,
x: Int,
y: Int,
@@ -39,9 +40,20 @@
flags: Int,
override val view: View
) : AdditionalViewContainer() {
+ val lp: WindowManager.LayoutParams = WindowManager.LayoutParams(
+ width, height, x, y,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ flags,
+ PixelFormat.TRANSPARENT
+ ).apply {
+ title = "Additional view container of Task=$taskId"
+ gravity = Gravity.LEFT or Gravity.TOP
+ setTrustedOverlay()
+ }
constructor(
context: Context,
+ windowManagerWrapper: WindowManagerWrapper,
taskId: Int,
x: Int,
y: Int,
@@ -50,7 +62,7 @@
flags: Int,
@LayoutRes layoutId: Int
) : this(
- context = context,
+ windowManagerWrapper = windowManagerWrapper,
taskId = taskId,
x = x,
y = y,
@@ -61,9 +73,16 @@
)
constructor(
- context: Context, taskId: Int, x: Int, y: Int, width: Int, height: Int, flags: Int
+ context: Context,
+ windowManagerWrapper: WindowManagerWrapper,
+ taskId: Int,
+ x: Int,
+ y: Int,
+ width: Int,
+ height: Int,
+ flags: Int
) : this(
- context = context,
+ windowManagerWrapper = windowManagerWrapper,
taskId = taskId,
x = x,
y = y,
@@ -73,24 +92,12 @@
view = View(context)
)
- val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java)
-
init {
- val lp = WindowManager.LayoutParams(
- width, height, x, y,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
- flags,
- PixelFormat.TRANSPARENT
- ).apply {
- title = "Additional view container of Task=$taskId"
- gravity = Gravity.LEFT or Gravity.TOP
- setTrustedOverlay()
- }
- windowManager?.addView(view, lp)
+ windowManagerWrapper.addView(view, lp)
}
override fun releaseView() {
- windowManager?.removeViewImmediate(view)
+ windowManagerWrapper.removeViewImmediate(view)
}
override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) {
@@ -98,6 +105,6 @@
this.x = x.toInt()
this.y = y.toInt()
}
- windowManager?.updateViewLayout(view, lp)
+ windowManagerWrapper.updateViewLayout(view, lp)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 510032b..9ef4b8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -29,9 +29,11 @@
import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
import android.view.WindowManager
import android.widget.ImageButton
+import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.shared.animation.Interpolators
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
/**
@@ -41,14 +43,14 @@
internal class AppHandleViewHolder(
rootView: View,
onCaptionTouchListener: View.OnTouchListener,
- onCaptionButtonClickListener: OnClickListener
+ onCaptionButtonClickListener: OnClickListener,
+ private val windowManagerWrapper: WindowManagerWrapper
) : WindowDecorationViewHolder(rootView) {
companion object {
private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100
}
private lateinit var taskInfo: RunningTaskInfo
- private val windowManager = context.getSystemService(WindowManager::class.java)
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
private val inputManager = context.getSystemService(InputManager::class.java)
@@ -73,7 +75,10 @@
) {
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
this.taskInfo = taskInfo
- if (!isCaptionVisible && hasStatusBarInputLayer()) {
+ // If handle is not in status bar region(i.e., bottom stage in vertical split),
+ // do not create an input layer
+ if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
+ if (!isCaptionVisible && hasStatusBarInputLayer() ) {
disposeStatusBarInputLayer()
return
}
@@ -96,11 +101,12 @@
handleWidth: Int,
handleHeight: Int) {
if (!Flags.enableAdditionalWindowsAboveStatusBar()) return
- statusBarInputLayer = AdditionalSystemViewContainer(context, taskInfo.taskId,
- handlePosition.x, handlePosition.y, handleWidth, handleHeight,
+ statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper,
+ taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
- val view = statusBarInputLayer?.view
- val lp = view?.layoutParams as WindowManager.LayoutParams
+ val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View")
+ val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer" +
+ "LayoutParams")
lp.title = "Handle Input Layer of task " + taskInfo.taskId
lp.setTrustedOverlay()
// Make this window a spy window to enable it to pilfer pointers from the system-wide
@@ -118,9 +124,9 @@
inputManager.pilferPointers(v.viewRootImpl.inputToken)
}
captionHandle.dispatchTouchEvent(event)
- true
+ return@setOnTouchListener true
}
- windowManager.updateViewLayout(view, lp)
+ windowManagerWrapper.updateViewLayout(view, lp)
}
private fun updateStatusBarInputLayer(globalPosition: Point) {
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 507ad64..7640cb1 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
@@ -153,6 +157,22 @@
assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS
)
+ val EDGE_RESIZE =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("EDGE_RESIZE"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
val CORNER_RESIZE_TO_MINIMUM_SIZE =
FlickerConfigEntry(
scenarioId = ScenarioId("CORNER_RESIZE_TO_MINIMUM_SIZE"),
@@ -165,10 +185,35 @@
.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 }),
)
+ val CORNER_RESIZE_TO_MAXIMUM_SIZE =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("CORNER_RESIZE_TO_MAXIMUM_SIZE"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions =
+ AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP),
+ AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
val SNAP_RESIZE_LEFT_WITH_BUTTON =
FlickerConfigEntry(
scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_BUTTON"),
@@ -284,5 +329,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/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
new file mode 100644
index 0000000..0b98ba2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
@@ -0,0 +1,52 @@
+/*
+ * 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
+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.CORNER_RESIZE_TO_MAXIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Resize app window using corner resize to the greatest possible height and width in
+ * landscape mode.
+ *
+ * Assert that the maximum window size constraint is maintained.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppToMaximumWindowSizeLandscape : ResizeAppWithCornerResize(
+ rotation = Rotation.ROTATION_90
+) {
+ @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"])
+ @Test
+ override fun resizeAppWithCornerResizeToMaximumSize() =
+ super.resizeAppWithCornerResizeToMaximumSize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MAXIMUM_SIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
new file mode 100644
index 0000000..b1c04d3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.CORNER_RESIZE_TO_MAXIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Resize app window using corner resize to the greatest possible height and width in
+ * portrait mode.
+ *
+ * Assert that the maximum window size constraint is maintained.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppToMaximumWindowSizePortrait : ResizeAppWithCornerResize() {
+ @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"])
+ @Test
+ override fun resizeAppWithCornerResizeToMaximumSize() =
+ super.resizeAppWithCornerResizeToMaximumSize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MAXIMUM_SIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
new file mode 100644
index 0000000..c3abf23
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeMouse : ResizeAppWithEdgeResize(InputMethod.MOUSE) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
new file mode 100644
index 0000000..86b0e6f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeStylus : ResizeAppWithEdgeResize(InputMethod.STYLUS) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
new file mode 100644
index 0000000..e6bb9ef
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeTouchpad : ResizeAppWithEdgeResize(InputMethod.TOUCHPAD) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt
new file mode 100644
index 0000000..a4dc52b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.CloseAllAppsWithAppHeaderExit
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [CloseAllAppsWithAppHeaderExit]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class CloseAllAppsWithAppHeaderExitTest() : CloseAllAppsWithAppHeaderExit()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt
index 06905379..3d95f97 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.shade.shared.model
+package com.android.wm.shell.functional
-/** Enumerates all supported alignments of the shade. */
-sealed interface ShadeAlignment {
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.EnterDesktopWithDrag
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
- /** Aligns the shade to the top. */
- data object Top : ShadeAlignment
-
- /** Aligns the shade to the bottom. */
- data object Bottom : ShadeAlignment
-}
+/* Functional test for [EnterDesktopWithDrag]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class EnterDesktopWithDragTest : EnterDesktopWithDrag()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt
new file mode 100644
index 0000000..140c5ec
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ExitDesktopWithDragToTopDragZone
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ExitDesktopWithDragToTopDragZone]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ExitDesktopWithDragToTopDragZoneTest : ExitDesktopWithDragToTopDragZone()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt
index 7e09a10..3d3dcd0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard
+package com.android.wm.shell.functional
-import com.android.systemui.Flags
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-/** Helper for reading or using the new picker UI flag. */
-@Suppress("NOTHING_TO_INLINE")
-object NewPickerUiKeyguardPreview {
-
- /** Is the new picker UI enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.newPickerUi()
-}
+/* Functional test for [MaximizeAppWindow]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class MaximizeAppWindowTest : MaximizeAppWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
copy to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt
index 7e09a10..263e89f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard
+package com.android.wm.shell.functional
-import com.android.systemui.Flags
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-/** Helper for reading or using the new picker UI flag. */
-@Suppress("NOTHING_TO_INLINE")
-object NewPickerUiKeyguardPreview {
-
- /** Is the new picker UI enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.newPickerUi()
-}
+/* Functional test for [OpenAppsInDesktopMode]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class OpenAppsInDesktopModeTest : OpenAppsInDesktopMode()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt
new file mode 100644
index 0000000..13f4775
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppCornerMultiWindowAndPip
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppCornerMultiWindowAndPip]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppCornerMultiWindowAndPipTest : ResizeAppCornerMultiWindowAndPip()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt
new file mode 100644
index 0000000..bc9bb41
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppCornerMultiWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppCornerMultiWindow]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppCornerMultiWindowTest : ResizeAppCornerMultiWindow()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt
new file mode 100644
index 0000000..46168eb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppWithCornerResize]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppWithCornerResizeTest : ResizeAppWithCornerResize()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt
new file mode 100644
index 0000000..ee24200
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.server.wm.flicker.helpers.MotionEventHelper
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppWithEdgeResize]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppWithEdgeResizeTest :
+ ResizeAppWithEdgeResize(MotionEventHelper.InputMethod.TOUCHPAD)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt
new file mode 100644
index 0000000..38e85c7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SnapResizeAppWindowWithButton]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SnapResizeAppWindowWithButtonTest : SnapResizeAppWindowWithButton()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt
new file mode 100644
index 0000000..082a3fb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SnapResizeAppWindowWithDrag]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SnapResizeAppWindowWithDragTest : SnapResizeAppWindowWithDrag()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt
new file mode 100644
index 0000000..fdd0d81
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SwitchToOverviewFromDesktop
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SwitchToOverviewFromDesktop]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SwitchToOverviewFromDesktopTest : SwitchToOverviewFromDesktop()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
index e9056f3..351a700 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -33,15 +32,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class CloseAllAppsWithAppHeaderExit
-@JvmOverloads
+@Ignore("Base Test Class")
+abstract class CloseAllAppsWithAppHeaderExit
constructor(val rotation: Rotation = Rotation.ROTATION_0) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt
new file mode 100644
index 0000000..5a69b27
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.tools.traces.parsers.WindowManagerStateHelper
+import android.tools.traces.parsers.toFlickerComponent
+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.LetterboxAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.Ignore
+
+/** Base test class for desktop CUJ with customizable test app. */
+@Ignore("Base Test Class")
+abstract class DesktopScenarioCustomAppTestBase(
+ isResizeable: Boolean = true,
+ isLandscapeApp: Boolean = true
+) {
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+ // TODO(b/363181411): Consolidate in LetterboxAppHelper.
+ val testApp = when {
+ isResizeable && isLandscapeApp -> SimpleAppHelper(instrumentation)
+ isResizeable && !isLandscapeApp -> SimpleAppHelper(
+ instrumentation,
+ launcherName = ActivityOptions.PortraitOnlyActivity.LABEL,
+ component = ActivityOptions.PortraitOnlyActivity.COMPONENT.toFlickerComponent()
+ )
+ // NonResizeablAppHelper has no fixed orientation.
+ !isResizeable && isLandscapeApp -> NonResizeableAppHelper(instrumentation)
+ // Opens NonResizeablePortraitActivity.
+ else -> LetterboxAppHelper(instrumentation)
+ }.let { DesktopModeAppHelper(it) }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
index ca1dc1a..3f9927f 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.MailAppHelper
@@ -26,13 +25,11 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
+@Ignore("Test Base Class")
+abstract class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
{
private val imeAppHelper = ImeAppHelper(instrumentation)
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
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 0f0d2df..967bd29 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
@@ -16,37 +16,25 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
-import android.app.Instrumentation
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.SimpleAppHelper
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
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.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class EnterDesktopWithDrag
-@JvmOverloads
-constructor(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 testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+@Ignore("Test Base Class")
+abstract class EnterDesktopWithDrag
+constructor(
+ val rotation: Rotation = Rotation.ROTATION_0,
+ isResizeable: Boolean = true,
+ isLandscapeApp: Boolean = true,
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
@Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
@@ -55,6 +43,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/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
index 533be88..824c448 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
@@ -16,37 +16,24 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
-import android.app.Instrumentation
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.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.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ExitDesktopWithDragToTopDragZone
-@JvmOverloads
-constructor(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 testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+@Ignore("Test Base Class")
+abstract class ExitDesktopWithDragToTopDragZone
+constructor(
+ val rotation: Rotation = Rotation.ROTATION_0,
+ isResizeable: Boolean = true,
+ isLandscapeApp: Boolean = true,
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
@Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
index b812c59..a54d497 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -32,15 +32,13 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class MaximizeAppWindow
-@JvmOverloads
-constructor(rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) {
+
+@Ignore("Test Base Class")
+abstract class MaximizeAppWindow
+constructor(private val rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -57,6 +55,9 @@
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
testApp.enterDesktopWithDrag(wmHelper, device)
}
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..aad266f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.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.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract 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)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
index b6bca7a9..bfee318 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -34,15 +33,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppCornerMultiWindow
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class ResizeAppCornerMultiWindow
constructor(val rotation: Rotation = Rotation.ROTATION_0,
val horizontalChange: Int = 50,
val verticalChange: Int = -50) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
index 285ea13..5b1b64e 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -35,15 +34,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppCornerMultiWindowAndPip
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class ResizeAppCornerMultiWindowAndPip
constructor(val rotation: Rotation = Rotation.ROTATION_0,
val horizontalChange: Int = 50,
val verticalChange: Int = -50) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
index 03d970f..bd25639 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -32,19 +31,15 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppWithCornerResize
-@JvmOverloads
-constructor(
+@Ignore("Test Base Class")
+abstract class ResizeAppWithCornerResize(
val rotation: Rotation = Rotation.ROTATION_0,
- val horizontalChange: Int = 50,
- val verticalChange: Int = -50,
+ val horizontalChange: Int = 200,
+ val verticalChange: Int = -200,
val appProperty: AppProperty = AppProperty.STANDARD
) {
@@ -83,6 +78,25 @@
)
}
+ @Test
+ open fun resizeAppWithCornerResizeToMaximumSize() {
+ val maxResizeChange = 3000
+ testApp.cornerResize(
+ wmHelper,
+ device,
+ DesktopModeAppHelper.Corners.RIGHT_TOP,
+ maxResizeChange,
+ -maxResizeChange
+ )
+ testApp.cornerResize(
+ wmHelper,
+ device,
+ DesktopModeAppHelper.Corners.LEFT_BOTTOM,
+ -maxResizeChange,
+ maxResizeChange
+ )
+ }
+
@After
fun teardown() {
testApp.exit(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
new file mode 100644
index 0000000..6780238
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.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.MotionEventHelper
+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.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract class ResizeAppWithEdgeResize
+constructor(
+ val inputMethod: MotionEventHelper.InputMethod,
+ val rotation: Rotation = Rotation.ROTATION_90
+) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val motionEventHelper = MotionEventHelper(instrumentation, inputMethod)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(
+ Flags.enableDesktopWindowingMode()
+ && Flags.enableWindowingEdgeDragResize() && tapl.isTablet
+ )
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ testApp.enterDesktopWithDrag(wmHelper, device)
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeRight() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.RIGHT
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeLeft() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.LEFT
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeTop() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.TOP
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeBottom() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.BOTTOM
+ )
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
index 685a3ba..2b40497 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
@@ -17,7 +17,8 @@
package com.android.wm.shell.scenarios
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+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
@@ -26,18 +27,17 @@
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.Ignore
+import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SnapResizeAppWindowWithButton
-@JvmOverloads
-constructor(private val toLeft: Boolean = true, private val isResizable: Boolean = true) {
+@Ignore("Test Base Class")
+abstract class SnapResizeAppWindowWithButton
+constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -49,6 +49,10 @@
DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
}
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
index 8a4aa63..b4bd7e1 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
@@ -17,7 +17,8 @@
package com.android.wm.shell.scenarios
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+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
@@ -26,18 +27,17 @@
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.Ignore
+import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SnapResizeAppWindowWithDrag
-@JvmOverloads
-constructor(private val toLeft: Boolean = true, private val isResizable: Boolean = true) {
+@Ignore("Test Base Class")
+abstract class SnapResizeAppWindowWithDrag
+constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -49,6 +49,10 @@
DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
}
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
@@ -64,4 +68,4 @@
fun teardown() {
testApp.exit(wmHelper)
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
index 53e36e23..dad2eb6 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -31,20 +30,17 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
/**
* Base test for opening recent apps overview from desktop mode.
*
* Navigation mode can be passed as a constructor parameter, by default it is set to gesture navigation.
*/
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SwitchToOverviewFromDesktop
-@JvmOverloads
+@Ignore("Base Test Class")
+abstract class SwitchToOverviewFromDesktop
constructor(val navigationMode: NavBar = NavBar.MODE_GESTURAL) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
index dee67f3..c0fafef 100644
--- a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell
import android.app.Instrumentation
+import android.platform.test.rule.EnsureDeviceSettingsRule
import android.platform.test.rule.NavigationModeRule
import android.platform.test.rule.PressHomeRule
import android.platform.test.rule.UnlockScreenRule
@@ -49,5 +50,6 @@
)
)
.around(PressHomeRule())
+ .around(EnsureDeviceSettingsRule())
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 4058fa9..58559ac 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -68,6 +68,7 @@
"flickerlib-helpers",
"flickerlib-trace_processor_shell",
"platform-test-annotations",
+ "platform-test-rules",
"wm-flicker-common-app-helpers",
"wm-flicker-common-assertions",
"launcher-helper-lib",
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS
new file mode 100644
index 0000000..a36a4f8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS
@@ -0,0 +1,2 @@
+# Window Manager > App Compat
+# Bug component: 970984
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index b85d793..a9ed13a 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,10 +16,14 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder
+import android.tools.flicker.subject.exceptions.IncorrectRegionException
+import android.tools.flicker.subject.layers.LayerSubject
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
@@ -29,6 +33,7 @@
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
+import kotlin.math.abs
/**
* Test entering pip from an app via auto-enter property when navigating to home.
@@ -67,9 +72,24 @@
override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } }
- @FlakyTest(bugId = 293133362)
+ private val widthNotSmallerThan: LayerSubject.(LayerSubject) -> Unit = {
+ val width = visibleRegion.region.bounds.width()
+ val otherWidth = it.visibleRegion.region.bounds.width()
+ if (width < otherWidth && abs(width - otherWidth) > EPSILON) {
+ val errorMsgBuilder =
+ ExceptionMessageBuilder()
+ .forSubject(this)
+ .forIncorrectRegion("width. $width smaller than $otherWidth")
+ .setExpected(width)
+ .setActual(otherWidth)
+ throw IncorrectRegionException(errorMsgBuilder)
+ }
+ }
+
+ @Postsubmit
@Test
override fun pipLayerReduces() {
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
flicker.assertLayers {
val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
@@ -78,6 +98,32 @@
}
}
+ /** Checks that [pipApp] window's width is first decreasing then increasing. */
+ @Postsubmit
+ @Test
+ fun pipLayerWidthDecreasesThenIncreases() {
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+ var previousLayer = pipLayerList[0]
+ var currentLayer = previousLayer
+ var i = 0
+ invoke("layer area is decreasing") {
+ if (i < pipLayerList.size - 1) {
+ previousLayer = currentLayer
+ currentLayer = pipLayerList[++i]
+ previousLayer.widthNotSmallerThan(currentLayer)
+ }
+ }.then().invoke("layer are is increasing", true /* isOptional */) {
+ if (i < pipLayerList.size - 1) {
+ previousLayer = currentLayer
+ currentLayer = pipLayerList[++i]
+ currentLayer.widthNotSmallerThan(previousLayer)
+ }
+ }
+ }
+ }
+
/** Checks that [pipApp] window is animated towards default position in right bottom corner */
@FlakyTest(bugId = 255578530)
@Test
@@ -108,4 +154,9 @@
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
+
+ companion object {
+ // TODO(b/363080056): A margin of error allowed on certain layer size calculations.
+ const val EPSILON = 1
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
index 70be58f..429774f 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -35,7 +35,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { pipApp.changeAspectRatio() }
+ transitions { pipApp.changeAspectRatio(wmHelper) }
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 9cbdcdb..049a5a0 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -94,3 +94,10 @@
"com.android.wm.shell.tests",
],
}
+
+test_module_config {
+ name: "WMShellUnitTests_shell_back",
+ base: "WMShellUnitTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.wm.shell.back"],
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 413e495..e514dc3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -49,7 +49,6 @@
import android.os.RemoteException;
import android.util.SparseArray;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
import android.window.TaskAppearedInfo;
@@ -169,7 +168,7 @@
public void testTaskLeashReleasedAfterVanished() throws RemoteException {
assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo taskInfo = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_MULTI_WINDOW);
- SurfaceControl taskLeash = new SurfaceControl.Builder(new SurfaceSession())
+ SurfaceControl taskLeash = new SurfaceControl.Builder()
.setName("task").build();
mOrganizer.registerOrganizer();
mOrganizer.onTaskAppeared(taskInfo, taskLeash);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
index 4998702..f31722d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
@@ -19,6 +19,7 @@
import com.android.wm.shell.common.ShellExecutor;
import java.util.ArrayList;
+import java.util.List;
/**
* Really basic test executor. It just gathers all events in a blob. The only option is to
@@ -52,4 +53,9 @@
mRunnables.remove(0).run();
}
}
+
+ /** Returns the list of callbacks for this executor. */
+ public List<Runnable> getCallbacks() {
+ return mRunnables;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 1e4b8b6..b53ea38 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -692,6 +692,8 @@
mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
verify(mBackTransitionHandler).handlePrepareTransition(
eq(tInfo), eq(st), eq(ft), eq(callback));
+
+ mBackTransitionHandler.onAnimationFinished();
final TransitionInfo.Change openToClose = createAppChange(openTaskId, TRANSIT_CLOSE,
FLAG_BACK_GESTURE_ANIMATED);
tInfo2 = createTransitionInfo(TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, openToClose);
@@ -700,7 +702,6 @@
mBackTransitionHandler.mergeAnimation(mBackTransitionHandler.mClosePrepareTransition,
tInfo2, st, mock(IBinder.class), mergeCallback);
assertTrue("Change should be consumed", tInfo2.getChanges().isEmpty());
- mBackTransitionHandler.onAnimationFinished();
verify(callback).onTransitionFinished(any());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 4d0348b..9b019dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -177,6 +177,31 @@
assertEquals(1, finishCallbackCalled.getCount());
}
+ @Test
+ public void testOnBackInvokedFinishCallbackNotInvokedWhenRemoved() throws InterruptedException {
+ // Give the animator some progress.
+ final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress);
+ mMainThreadHandler.post(
+ () -> mProgressAnimator.onBackProgressed(backEvent));
+ mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+ assertNotNull(mReceivedBackEvent);
+
+ // Trigger back invoked animation
+ CountDownLatch finishCallbackCalled = new CountDownLatch(1);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> mProgressAnimator.onBackInvoked(finishCallbackCalled::countDown));
+
+ // remove onBackCancelled finishCallback (while progress is still animating to 0)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> mProgressAnimator.removeOnBackInvokedFinishCallback());
+
+ // call reset (which triggers the finishCallback invocation, if one is present)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mProgressAnimator.reset());
+
+ // verify that finishCallback is not invoked
+ assertEquals(1, finishCallbackCalled.getCount());
+ }
+
private void onGestureProgress(BackEvent backEvent) {
if (mTargetProgress == backEvent.getProgress()) {
mReceivedBackEvent = backEvent;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 859602e..6fa3788 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -50,8 +50,8 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.bubbles.BubbleData.TimeSource;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.google.common.collect.ImmutableList;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
index 50c4a18..dca5fc4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
@@ -43,7 +43,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 09fcd8b..82b3a7d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -20,8 +20,6 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50;
-import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
-import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
import static com.google.common.truth.Truth.assertThat;
@@ -150,8 +148,8 @@
@UiThreadTest
public void testSnapToDismissStart() {
// verify it callbacks properly when the snap target indicates dismissing split.
- DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
- SNAP_TO_START_AND_DISMISS);
+ DividerSnapAlgorithm.SnapTarget snapTarget =
+ mSplitLayout.mDividerSnapAlgorithm.getDismissStartTarget();
mSplitLayout.snapToTarget(mSplitLayout.getDividerPosition(), snapTarget);
waitDividerFlingFinished();
@@ -162,8 +160,8 @@
@UiThreadTest
public void testSnapToDismissEnd() {
// verify it callbacks properly when the snap target indicates dismissing split.
- DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
- SNAP_TO_END_AND_DISMISS);
+ DividerSnapAlgorithm.SnapTarget snapTarget =
+ mSplitLayout.mDividerSnapAlgorithm.getDismissEndTarget();
mSplitLayout.snapToTarget(mSplitLayout.getDividerPosition(), snapTarget);
waitDividerFlingFinished();
@@ -203,9 +201,4 @@
new Rect(0, 0, 1080, 2160));
return configuration;
}
-
- private static DividerSnapAlgorithm.SnapTarget getSnapTarget(int position, int flag) {
- return new DividerSnapAlgorithm.SnapTarget(
- position /* position */, position /* taskPosition */, flag);
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index f558e87..2b7f86f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -22,6 +22,7 @@
import android.testing.AndroidTestingRunner
import android.view.SurfaceControl
import androidx.test.filters.SmallTest
+import com.android.internal.policy.SystemBarUtils
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
@@ -67,8 +68,7 @@
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
- val transitionHeight = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_transition_region_thickness)
+ val transitionHeight = SystemBarUtils.getStatusBarHeight(context)
val toFullscreenScale = mContext.resources.getFloat(
R.dimen.desktop_mode_fullscreen_region_scale
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 5474e53..10557dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -123,12 +123,12 @@
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.eq
@@ -2859,7 +2859,7 @@
}
@Test
- fun getSnapBounds_calculatesBoundsForResizable() {
+ fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
val bounds = Rect(100, 100, 300, 300)
val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
topActivityInfo = ActivityInfo().apply {
@@ -2874,13 +2874,45 @@
STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
)
- controller.snapToHalfScreen(task, currentDragBounds, SnapPosition.LEFT)
+ controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)
// Assert bounds set to stable bounds
val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
}
@Test
+ fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ // Set up task to already be in snapped-left bounds
+ val bounds = Rect(
+ STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
+ )
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+ topActivityInfo = ActivityInfo().apply {
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+ configuration.windowConfiguration.appBounds = bounds
+ }
+ isResizeable = true
+ }
+
+ // Attempt to snap left again
+ val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
+ controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)
+
+ // Assert that task is NOT updated via WCT
+ verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+
+ // Assert that task leash is updated via Surface Animations
+ verify(mReturnToDragStartAnimator).start(
+ eq(task.taskId),
+ eq(mockSurface),
+ eq(currentDragBounds),
+ eq(bounds),
+ eq(true)
+ )
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun handleSnapResizingTask_nonResizable_snapsToHalfScreen() {
val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
@@ -2911,7 +2943,8 @@
eq(task.taskId),
eq(mockSurface),
eq(currentDragBounds),
- eq(preDragBounds)
+ eq(preDragBounds),
+ eq(false)
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index a8d40db..386253c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -46,6 +46,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -136,6 +137,8 @@
mMainExecutor = new TestShellExecutor();
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
+ when(mContext.getSystemService(KeyguardManager.class))
+ .thenReturn(mock(KeyguardManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
mDisplayInsetsController, mMainExecutor));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
index 27e0b19..b9bf95b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.DEFAULT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.DEFAULT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
similarity index 96%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
index 5b22edd..641063c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.os.Parcel
import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE
@@ -41,6 +41,7 @@
"com.some.package",
"title",
"Some app",
+ true,
true
)
val parcel = Parcel.obtain()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt
similarity index 75%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt
index 2a6754c..d3e291f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.shared.navigationbar
+package com.android.wm.shell.shared.handles
+
import android.graphics.Rect
import android.testing.TestableLooper.RunWithLooper
@@ -24,16 +25,15 @@
import androidx.concurrent.futures.DirectExecutor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.time.FakeSystemClock
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
@@ -42,11 +42,12 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.argumentCaptor
@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
-class RegionSamplingHelperTest : SysuiTestCase() {
+class RegionSamplingHelperTest : ShellTestCase() {
@Mock
lateinit var sampledView: View
@@ -72,12 +73,16 @@
whenever(surfaceControl.isValid).thenReturn(true)
whenever(wrappedSurfaceControl.isValid).thenReturn(true)
whenever(samplingCallback.isSamplingEnabled).thenReturn(true)
- regionSamplingHelper = object : RegionSamplingHelper(sampledView, samplingCallback,
- DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener) {
- override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
- return wrappedSurfaceControl
+ getInstrumentation().runOnMainSync(Runnable {
+ regionSamplingHelper = object : RegionSamplingHelper(
+ sampledView, samplingCallback,
+ DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener
+ ) {
+ override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
+ return wrappedSurfaceControl
+ }
}
- }
+ })
regionSamplingHelper.setWindowVisible(true)
}
@@ -99,7 +104,7 @@
regionSamplingHelper.setWindowHasBlurs(true)
regionSamplingHelper.start(Rect(0, 0, 100, 100))
verify(compositionListener, never())
- .register(any(), anyInt(), eq(wrappedSurfaceControl), any())
+ .register(any(), anyInt(), eq(wrappedSurfaceControl), any())
}
@Test
@@ -112,35 +117,38 @@
@Test
fun testCompositionSamplingListener_has_nonEmptyRect() {
// simulate race condition
- val fakeExecutor = FakeExecutor(FakeSystemClock()) // pass in as backgroundExecutor
+ val fakeExecutor = TestShellExecutor() // pass in as backgroundExecutor
val fakeSamplingCallback = mock(RegionSamplingHelper.SamplingCallback::class.java)
whenever(fakeSamplingCallback.isSamplingEnabled).thenReturn(true)
whenever(wrappedSurfaceControl.isValid).thenReturn(true)
-
- regionSamplingHelper = object : RegionSamplingHelper(sampledView, fakeSamplingCallback,
- DirectExecutor.INSTANCE, fakeExecutor, compositionListener) {
- override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
- return wrappedSurfaceControl
+ getInstrumentation().runOnMainSync(Runnable {
+ regionSamplingHelper = object : RegionSamplingHelper(
+ sampledView, fakeSamplingCallback,
+ DirectExecutor.INSTANCE, fakeExecutor, compositionListener
+ ) {
+ override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
+ return wrappedSurfaceControl
+ }
}
- }
+ })
regionSamplingHelper.setWindowVisible(true)
regionSamplingHelper.start(Rect(0, 0, 100, 100))
// make sure background task is enqueued
- assertThat(fakeExecutor.numPending()).isEqualTo(1)
+ assertThat(fakeExecutor.getCallbacks().size).isEqualTo(1)
// make sure regionSamplingHelper will have empty Rect
whenever(fakeSamplingCallback.getSampledRegion(any())).thenReturn(Rect(0, 0, 0, 0))
regionSamplingHelper.onLayoutChange(sampledView, 0, 0, 0, 0, 0, 0, 0, 0)
// resume running of background thread
- fakeExecutor.runAllReady()
+ fakeExecutor.flushAll()
// grab Rect passed into compositionSamplingListener and make sure it's not empty
val argumentGrabber = argumentCaptor<Rect>()
verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl),
- argumentGrabber.capture())
- assertThat(argumentGrabber.value.isEmpty).isFalse()
+ argumentGrabber.capture())
+ assertThat(argumentGrabber.firstValue.isEmpty).isFalse()
}
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
deleted file mode 100644
index b1befc4..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/** Tests for {@link MainStage} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MainStageTests extends ShellTestCase {
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
- @Mock private SurfaceControl mRootLeash;
- @Mock private IconProvider mIconProvider;
- private WindowContainerTransaction mWct = new WindowContainerTransaction();
- private SurfaceSession mSurfaceSession = new SurfaceSession();
- private MainStage mMainStage;
-
- @Before
- @UiThreadTest
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
- mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty());
- mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
- }
-
- @Test
- public void testActiveDeactivate() {
- mMainStage.activate(mWct, true /* reparent */);
- assertThat(mMainStage.isActive()).isTrue();
-
- mMainStage.deactivate(mWct);
- assertThat(mMainStage.isActive()).isFalse();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
deleted file mode 100644
index 549bd3f..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-
-import java.util.Optional;
-
-/** Tests for {@link SideStage} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SideStageTests extends ShellTestCase {
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private ActivityManager.RunningTaskInfo mRootTask;
- @Mock private SurfaceControl mRootLeash;
- @Mock private IconProvider mIconProvider;
- @Spy private WindowContainerTransaction mWct;
- private SurfaceSession mSurfaceSession = new SurfaceSession();
- private SideStage mSideStage;
-
- @Before
- @UiThreadTest
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mRootTask = new TestRunningTaskInfoBuilder().build();
- mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty());
- mSideStage.onTaskAppeared(mRootTask, mRootLeash);
- }
-
- @Test
- public void testAddTask() {
- final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
-
- mSideStage.addTask(task, mWct);
-
- verify(mWct).reparent(eq(task.token), eq(mRootTask.token), eq(true));
- }
-
- @Test
- public void testRemoveTask() {
- final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
- assertThat(mSideStage.removeTask(task.taskId, null, mWct)).isFalse();
-
- mSideStage.mChildrenTaskInfo.put(task.taskId, task);
- assertThat(mSideStage.removeTask(task.taskId, null, mWct)).isTrue();
- verify(mWct).reparent(eq(task.token), isNull(), eq(false));
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index aa96c45..66dcef6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -24,7 +24,6 @@
import android.graphics.Rect;
import android.os.Handler;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -74,10 +73,10 @@
final SurfaceControl mRootLeash;
TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage,
- DisplayController displayController, DisplayImeController imeController,
- DisplayInsetsController insetsController, SplitLayout splitLayout,
- Transitions transitions, TransactionPool transactionPool,
+ ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
+ StageTaskListener sideStage, DisplayController displayController,
+ DisplayImeController imeController, DisplayInsetsController insetsController,
+ SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
ShellExecutor mainExecutor, Handler mainHandler,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
@@ -89,7 +88,7 @@
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
- mRootLeash = new SurfaceControl.Builder(new SurfaceSession()).setName("test").build();
+ mRootLeash = new SurfaceControl.Builder().setName("test").build();
onTaskAppeared(mRootTask, mRootLeash);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index abe3dcc..ce3944a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -53,7 +53,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.window.IRemoteTransition;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
@@ -106,7 +105,6 @@
@Mock private DisplayInsetsController mDisplayInsetsController;
@Mock private TransactionPool mTransactionPool;
@Mock private Transitions mTransitions;
- @Mock private SurfaceSession mSurfaceSession;
@Mock private IconProvider mIconProvider;
@Mock private WindowDecorViewModel mWindowDecorViewModel;
@Mock private ShellExecutor mMainExecutor;
@@ -116,8 +114,8 @@
@Mock private SplitScreen.SplitInvocationListener mInvocationListener;
private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
private SplitLayout mSplitLayout;
- private MainStage mMainStage;
- private SideStage mSideStage;
+ private StageTaskListener mMainStage;
+ private StageTaskListener mSideStage;
private StageCoordinator mStageCoordinator;
private SplitScreenTransitions mSplitScreenTransitions;
@@ -133,12 +131,12 @@
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
- mMainStage = spy(new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
+ mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
mIconProvider, Optional.of(mWindowDecorViewModel)));
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
- mSideStage = spy(new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
+ mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
mIconProvider, Optional.of(mWindowDecorViewModel)));
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 0054cb6..a6c16c4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -50,7 +50,6 @@
import android.os.Handler;
import android.os.Looper;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.window.RemoteTransition;
import android.window.WindowContainerTransaction;
@@ -97,9 +96,9 @@
@Mock
private SyncTransactionQueue mSyncQueue;
@Mock
- private MainStage mMainStage;
+ private StageTaskListener mMainStage;
@Mock
- private SideStage mSideStage;
+ private StageTaskListener mSideStage;
@Mock
private SplitLayout mSplitLayout;
@Mock
@@ -119,7 +118,6 @@
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
private final Rect mRootBounds = new Rect(0, 0, 45, 60);
- private SurfaceSession mSurfaceSession = new SurfaceSession();
private SurfaceControl mRootLeash;
private SurfaceControl mDividerLeash;
private ActivityManager.RunningTaskInfo mRootTask;
@@ -139,7 +137,7 @@
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
Optional.empty()));
- mDividerLeash = new SurfaceControl.Builder(mSurfaceSession).setName("fakeDivider").build();
+ mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
@@ -149,7 +147,7 @@
when(mSplitLayout.getDividerLeash()).thenReturn(mDividerLeash);
mRootTask = new TestRunningTaskInfoBuilder().build();
- mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
+ mRootLeash = new SurfaceControl.Builder().setName("test").build();
mStageCoordinator.onTaskAppeared(mRootTask, mRootLeash);
mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 946a7ef..b7b7d0d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -25,13 +25,13 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.os.SystemProperties;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
import androidx.test.annotation.UiThreadTest;
@@ -52,6 +52,7 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.util.Optional;
@@ -76,9 +77,10 @@
private IconProvider mIconProvider;
@Mock
private WindowDecorViewModel mWindowDecorViewModel;
+ @Spy
+ private WindowContainerTransaction mWct;
@Captor
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
- private SurfaceSession mSurfaceSession = new SurfaceSession();
private SurfaceControl mSurfaceControl;
private ActivityManager.RunningTaskInfo mRootTask;
private StageTaskListener mStageTaskListener;
@@ -93,12 +95,11 @@
DEFAULT_DISPLAY,
mCallbacks,
mSyncQueue,
- mSurfaceSession,
mIconProvider,
Optional.of(mWindowDecorViewModel));
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
- mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
+ mSurfaceControl = new SurfaceControl.Builder().setName("test").build();
mStageTaskListener.onTaskAppeared(mRootTask, mSurfaceControl);
}
@@ -177,4 +178,31 @@
mStageTaskListener.evictAllChildren(wct);
assertFalse(wct.isEmpty());
}
+
+ @Test
+ public void testAddTask() {
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+ mStageTaskListener.addTask(task, mWct);
+
+ verify(mWct).reparent(eq(task.token), eq(mRootTask.token), eq(true));
+ }
+
+ @Test
+ public void testRemoveTask() {
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+ assertThat(mStageTaskListener.removeTask(task.taskId, null, mWct)).isFalse();
+
+ mStageTaskListener.mChildrenTaskInfo.put(task.taskId, task);
+ assertThat(mStageTaskListener.removeTask(task.taskId, null, mWct)).isTrue();
+ verify(mWct).reparent(eq(task.token), isNull(), eq(false));
+ }
+
+ @Test
+ public void testActiveDeactivate() {
+ mStageTaskListener.activate(mWct, true /* reparent */);
+ assertThat(mStageTaskListener.isActive()).isTrue();
+
+ mStageTaskListener.deactivate(mWct);
+ assertThat(mStageTaskListener.isActive()).isFalse();
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 0434742..17fd95b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -49,7 +49,6 @@
import android.testing.TestableLooper;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
-import android.view.SurfaceSession;
import android.view.ViewTreeObserver;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -95,7 +94,6 @@
Looper mViewLooper;
TestHandler mViewHandler;
- SurfaceSession mSession;
SurfaceControl mLeash;
Context mContext;
@@ -106,7 +104,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mLeash = new SurfaceControl.Builder(mSession)
+ mLeash = new SurfaceControl.Builder()
.setName("test")
.build();
@@ -294,16 +292,6 @@
}
@Test
- public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() {
- assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
- verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
-
- mTaskViewTaskController.onTaskVanished(mTaskInfo);
- verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
- }
-
- @Test
public void testOnNewTask_noSurface() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -443,19 +431,6 @@
}
@Test
- public void testUnsetOnBackPressedOnTaskRoot() {
- assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
- WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
- new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
- mLeash, wct);
- verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
-
- mTaskViewTaskController.prepareCloseAnimation();
- verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
- }
-
- @Test
public void testSetObscuredTouchRect() {
mTaskView.setObscuredTouchRect(
new Rect(/* left= */ 0, /* top= */ 10, /* right= */ 100, /* bottom= */ 120));
@@ -713,4 +688,26 @@
verify(mViewHandler).post(any());
verify(mTaskView).setResizeBackgroundColor(eq(Color.BLUE));
}
+
+ @Test
+ public void testOnAppeared_setsTrimmableTask() {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+
+ assertThat(wct.getHierarchyOps().get(0).isTrimmableFromRecents()).isFalse();
+ }
+
+ @Test
+ public void testMoveToFullscreen_callsTaskRemovalStarted() {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskViewTaskController.moveToFullscreen();
+
+ verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index d2adae1..8f49de0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
@@ -38,6 +39,10 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
@@ -46,6 +51,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -57,6 +63,7 @@
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -70,6 +77,8 @@
@RunWith(AndroidJUnit4.class)
public class HomeTransitionObserverTest extends ShellTestCase {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
private final Context mContext =
@@ -187,6 +196,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_MIGRATE_PREDICTIVE_BACK_TRANSITION)
public void testHomeActivityWithBackGestureNotifiesHomeIsVisible() throws RemoteException {
TransitionInfo info = mock(TransitionInfo.class);
TransitionInfo.Change change = mock(TransitionInfo.Change.class);
@@ -205,6 +215,35 @@
verify(mListener, times(1)).onHomeVisibilityChanged(true);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MIGRATE_PREDICTIVE_BACK_TRANSITION)
+ public void testHomeActivityWithBackGestureNotifiesHomeIsVisibleAfterClose()
+ throws RemoteException {
+ TransitionInfo info = mock(TransitionInfo.class);
+ TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ when(change.getTaskInfo()).thenReturn(taskInfo);
+ when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+ when(info.getType()).thenReturn(TRANSIT_PREPARE_BACK_NAVIGATION);
+
+ when(change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)).thenReturn(true);
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+
+ mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean());
+
+ when(info.getType()).thenReturn(TRANSIT_TO_BACK);
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_CHANGE, true);
+ mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ verify(mListener, times(1)).onHomeVisibilityChanged(true);
+ }
+
/**
* Helper class to initialize variables for the rest.
*/
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 7c63fda..7937a84 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -76,7 +76,6 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import android.util.Pair;
-import android.view.IRecentsAnimationRunner;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
@@ -107,6 +106,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.recents.IRecentsAnimationRunner;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.shared.ShellSharedConstants;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 0b5c678..2c805e8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -34,6 +34,7 @@
import android.hardware.input.InputManager
import android.net.Uri
import android.os.Handler
+import android.os.SystemClock
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
@@ -52,10 +53,12 @@
import android.view.InsetsSource
import android.view.InsetsState
import android.view.KeyEvent
+import android.view.MotionEvent
import android.view.Surface
import android.view.SurfaceControl
import android.view.SurfaceView
import android.view.View
+import android.view.ViewRootImpl
import android.view.WindowInsets.Type.statusBars
import android.widget.Toast
import android.window.WindowContainerTransaction
@@ -73,6 +76,7 @@
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser
+import com.android.wm.shell.apptoweb.AssistContentRequester
import com.android.wm.shell.common.DisplayChangeController
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
@@ -113,7 +117,7 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argThat
@@ -165,6 +169,7 @@
@Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser
@Mock private lateinit var mockUserHandle: UserHandle
+ @Mock private lateinit var mockAssistContentRequester: AssistContentRequester
@Mock private lateinit var mockToast: Toast
private val bgExecutor = TestShellExecutor()
@Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper
@@ -172,6 +177,10 @@
@Mock private lateinit var mockFreeformTaskTransitionStarter: FreeformTaskTransitionStarter
@Mock private lateinit var mockActivityOrientationChangeHandler:
DesktopActivityOrientationChangeHandler
+ @Mock private lateinit var mockInputManager: InputManager
+ @Mock private lateinit var mockTaskPositionerFactory:
+ DesktopModeWindowDecorViewModel.TaskPositionerFactory
+ @Mock private lateinit var mockTaskPositioner: TaskPositioner
private lateinit var spyContext: TestableContext
private val transactionFactory = Supplier<SurfaceControl.Transaction> {
@@ -201,6 +210,7 @@
doNothing().`when`(spyContext).startActivity(any())
shellInit = ShellInit(mockShellExecutor)
windowDecorByTaskIdSpy.clear()
+ spyContext.addMockSystemService(InputManager::class.java, mockInputManager)
desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
spyContext,
mockShellExecutor,
@@ -218,6 +228,7 @@
mockTransitions,
Optional.of(mockDesktopTasksController),
mockGenericLinksParser,
+ mockAssistContentRequester,
mockMultiInstanceHelper,
mockDesktopModeWindowDecorFactory,
mockInputMonitorFactory,
@@ -226,12 +237,15 @@
windowDecorByTaskIdSpy,
mockInteractionJankMonitor,
Optional.of(mockTasksLimiter),
- Optional.of(mockActivityOrientationChangeHandler)
+ Optional.of(mockActivityOrientationChangeHandler),
+ mockTaskPositionerFactory
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor)
+ whenever(mockTaskPositionerFactory.create(any(), any(), any(), any(), any(), any(), any()))
+ .thenReturn(mockTaskPositioner)
doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
@@ -600,6 +614,7 @@
@Test
fun testOnDecorSnappedLeft_snapResizes() {
+ val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -610,8 +625,13 @@
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onLeftSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.LEFT)
+ )
+ assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface)
}
@Test
@@ -632,6 +652,7 @@
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun testOnSnapResizeLeft_nonResizable_decorSnappedLeft() {
+ val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -642,8 +663,13 @@
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onLeftSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.LEFT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -660,12 +686,13 @@
onLeftSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT))
verify(mockToast).show()
}
@Test
fun testOnDecorSnappedRight_snapResizes() {
+ val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -676,8 +703,13 @@
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onRightSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.RIGHT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -698,6 +730,7 @@
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun testOnSnapResizeRight_nonResizable_decorSnappedRight() {
+ val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -708,8 +741,13 @@
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onRightSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.RIGHT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -726,7 +764,7 @@
onRightSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT))
verify(mockToast).show()
}
@@ -1031,8 +1069,58 @@
verify(wct, never()).setBounds(eq(thirdTask.token), any())
}
+ @Test
+ fun testCloseButtonInFreeform_closeWindow_ignoreMoveEventsWithoutBoundsChange() {
+ val onClickListenerCaptor = forClass(View.OnClickListener::class.java)
+ as ArgumentCaptor<View.OnClickListener>
+ val onTouchListenerCaptor = forClass(View.OnTouchListener::class.java)
+ as ArgumentCaptor<View.OnTouchListener>
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onCaptionButtonClickListener = onClickListenerCaptor,
+ onCaptionButtonTouchListener = onTouchListenerCaptor
+ )
+
+ whenever(mockTaskPositioner.onDragPositioningStart(any(), any(), any()))
+ .thenReturn(INITIAL_BOUNDS)
+ whenever(mockTaskPositioner.onDragPositioningMove(any(), any()))
+ .thenReturn(INITIAL_BOUNDS)
+ whenever(mockTaskPositioner.onDragPositioningEnd(any(), any()))
+ .thenReturn(INITIAL_BOUNDS)
+
+ val view = mock(View::class.java)
+ whenever(view.id).thenReturn(R.id.close_window)
+ val viewRootImpl = mock(ViewRootImpl::class.java)
+ whenever(view.getViewRootImpl()).thenReturn(viewRootImpl)
+ whenever(viewRootImpl.getInputToken()).thenReturn(null)
+
+ desktopModeWindowDecorViewModel
+ .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter)
+
+ onTouchListenerCaptor.value.onTouch(view,
+ MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_DOWN, /* x= */ 0f, /* y= */ 0f, /* metaState= */ 0))
+ onTouchListenerCaptor.value.onTouch(view,
+ MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_MOVE, /* x= */ 0f, /* y= */ 0f, /* metaState= */ 0))
+ onTouchListenerCaptor.value.onTouch(view,
+ MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_UP, /* x= */ 0f, /* y= */ 0f, /* metaState= */ 0))
+ onClickListenerCaptor.value.onClick(view)
+
+ val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
+ verify(mockFreeformTaskTransitionStarter).startRemoveTransition(transactionCaptor.capture())
+ val wct = transactionCaptor.firstValue
+
+ assertEquals(1, wct.getHierarchyOps().size)
+ assertEquals(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK,
+ wct.getHierarchyOps().get(0).getType())
+ assertEquals(decor.mTaskInfo.token.asBinder(), wct.getHierarchyOps().get(0).getContainer())
+ }
+
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
+ taskSurface: SurfaceControl = SurfaceControl(),
onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =
forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
@@ -1048,10 +1136,12 @@
onOpenInBrowserClickListener: ArgumentCaptor<Consumer<Uri>> =
forClass(Consumer::class.java) as ArgumentCaptor<Consumer<Uri>>,
onCaptionButtonClickListener: ArgumentCaptor<View.OnClickListener> =
- forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener>
+ forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener>,
+ onCaptionButtonTouchListener: ArgumentCaptor<View.OnTouchListener> =
+ forClass(View.OnTouchListener::class.java) as ArgumentCaptor<View.OnTouchListener>
): DesktopModeWindowDecoration {
val decor = setUpMockDecorationForTask(createTask(windowingMode = windowingMode))
- onTaskOpening(decor.mTaskInfo)
+ onTaskOpening(decor.mTaskInfo, taskSurface)
verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())
verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())
verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())
@@ -1060,7 +1150,8 @@
verify(decor).setOnToSplitScreenClickListener(onToSplitScreenClickListenerCaptor.capture())
verify(decor).setOpenInBrowserClickListener(onOpenInBrowserClickListener.capture())
verify(decor).setCaptionListeners(
- onCaptionButtonClickListener.capture(), any(), any(), any())
+ onCaptionButtonClickListener.capture(), onCaptionButtonTouchListener.capture(),
+ any(), any())
return decor
}
@@ -1106,7 +1197,7 @@
whenever(
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
- any(), any(), any())
+ any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.isFocused).thenReturn(task.isFocused)
@@ -1147,5 +1238,6 @@
companion object {
private const val TAG = "DesktopModeWindowDecorViewModelTests"
private val STABLE_INSETS = Rect(0, 100, 0, 0)
+ private val INITIAL_BOUNDS = Rect(0, 0, 100, 100)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 596adfb..b9e542a0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -46,6 +46,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.assist.AssistContent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -88,6 +89,7 @@
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
@@ -133,6 +135,7 @@
private static final Uri TEST_URI1 = Uri.parse("https://www.google.com/");
private static final Uri TEST_URI2 = Uri.parse("https://docs.google.com/");
+ private static final Uri TEST_URI3 = Uri.parse("https://slides.google.com/");
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -173,9 +176,14 @@
@Mock
private AppToWebGenericLinksParser mMockGenericLinksParser;
@Mock
+ private WindowManager mMockWindowManager;
+ @Mock
+ private AssistContentRequester mMockAssistContentRequester;
+ @Mock
private HandleMenu mMockHandleMenu;
@Mock
private HandleMenuFactory mMockHandleMenuFactory;
+ @Mock
private MultiInstanceHelper mMockMultiInstanceHelper;
@Captor
private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
@@ -186,7 +194,8 @@
private SurfaceControl.Transaction mMockTransaction;
private StaticMockitoSession mMockitoSession;
private TestableContext mTestableContext;
- private ShellExecutor mBgExecutor = new TestShellExecutor();
+ private final ShellExecutor mBgExecutor = new TestShellExecutor();
+ private final AssistContent mAssistContent = new AssistContent();
/** Set up run before test class. */
@BeforeClass
@@ -220,9 +229,10 @@
final Display defaultDisplay = mock(Display.class);
doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
- when(mMockHandleMenuFactory.create(any(), anyInt(), any(), any(),
+ when(mMockHandleMenuFactory.create(any(), any(), anyInt(), any(), any(),
any(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), anyInt()))
.thenReturn(mMockHandleMenu);
+ when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
}
@After
@@ -385,6 +395,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void updateRelayoutParams_fullscreen_inputChannelNotNeeded() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -401,6 +412,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void updateRelayoutParams_multiwindow_inputChannelNotNeeded() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -491,7 +503,7 @@
.isTrue();
}
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
+ @Test
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -518,7 +530,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -530,7 +541,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_fullscreenTask_postsViewHostCreation() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -559,7 +569,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_removesExistingHandlerCallback() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -574,7 +583,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void close_removesExistingHandlerCallback() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -671,10 +679,11 @@
public void capturedLink_handleMenuBrowserLinkSetToCapturedLinkIfValid() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
+ TEST_URI3 /* generic link */);
// Verify handle menu's browser link set as captured link
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI1);
}
@@ -683,7 +692,8 @@
public void capturedLink_postsOnCapturedLinkExpiredRunnable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Run runnable to set captured link to expired
@@ -692,7 +702,7 @@
// Verify captured link is no longer valid by verifying link is not set as handle menu
// browser link.
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -701,7 +711,8 @@
public void capturedLink_capturedLinkNotResetToSameLink() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Run runnable to set captured link to expired
@@ -712,7 +723,7 @@
decor.relayout(taskInfo);
// Verify handle menu's browser link not set to captured link since link is expired
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -721,11 +732,12 @@
public void capturedLink_capturedLinkStillUsedIfExpiredAfterHandleMenuCreation() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Create handle menu before link expires
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
// Run runnable to set captured link to expired
verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
@@ -733,7 +745,7 @@
// Verify handle menu's browser link is set to captured link since menu was opened before
// captured link expired
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI1);
}
@@ -742,12 +754,13 @@
public void capturedLink_capturedLinkExpiresAfterClick() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Function1<Uri, Unit>> openInBrowserCaptor =
ArgumentCaptor.forClass(Function1.class);
// Simulate menu opening and clicking open in browser button
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -761,7 +774,7 @@
// Verify handle menu's browser link not set to captured link since link not valid after
// open in browser clicked
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -770,10 +783,11 @@
public void capturedLink_openInBrowserListenerCalledOnClick() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Function1<Uri, Unit>> openInBrowserCaptor =
ArgumentCaptor.forClass(Function1.class);
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -791,24 +805,38 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void genericLink_genericLinkUsedWhenCapturedLinkUnavailable() {
+ public void webUriLink_webUriLinkUsedWhenCapturedLinkUnavailable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, null /* captured link */, TEST_URI2 /* generic link */);
-
- // Verify handle menu's browser link set as generic link no captured link is available
- decor.createHandleMenu(mMockSplitScreenController);
+ taskInfo, null /* captured link */, TEST_URI2 /* web uri */,
+ TEST_URI3 /* generic link */);
+ // Verify handle menu's browser link set as web uri link when captured link is unavailable
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI2);
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+ public void genericLink_genericLinkUsedWhenCapturedLinkAndWebUriUnavailable() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ final DesktopModeWindowDecoration decor = createWindowDecoration(
+ taskInfo, null /* captured link */, null /* web uri */,
+ TEST_URI3 /* generic link */);
+
+ // Verify handle menu's browser link set as generic link when captured link and web uri link
+ // are unavailable
+ createHandleMenu(decor);
+ verifyHandleMenuCreated(TEST_URI3);
+ }
+
+ @Test
public void handleMenu_onCloseMenuClick_closesMenu() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
true /* relayout */);
final ArgumentCaptor<Function0<Unit>> closeClickListener =
ArgumentCaptor.forClass(Function0.class);
- decoration.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decoration);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -826,7 +854,7 @@
}
private void verifyHandleMenuCreated(@Nullable Uri uri) {
- verify(mMockHandleMenuFactory).create(any(), anyInt(), any(), any(),
+ verify(mMockHandleMenuFactory).create(any(), any(), anyInt(), any(), any(),
any(), anyBoolean(), anyBoolean(), eq(uri), anyInt(), anyInt(), anyInt());
}
@@ -858,9 +886,10 @@
private DesktopModeWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, @Nullable Uri capturedLink,
- @Nullable Uri genericLink) {
+ @Nullable Uri webUri, @Nullable Uri genericLink) {
taskInfo.capturedLink = capturedLink;
taskInfo.capturedLinkTimestamp = System.currentTimeMillis();
+ mAssistContent.setWebUri(webUri);
final String genericLinkString = genericLink == null ? null : genericLink.toString();
doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any());
// Relayout to set captured link
@@ -892,10 +921,10 @@
mContext, mMockDisplayController, mMockSplitScreenController,
mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mMockHandler, mBgExecutor,
mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
- mMockGenericLinksParser, SurfaceControl.Builder::new, mMockTransactionSupplier,
- WindowContainerTransaction::new, SurfaceControl::new,
- mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory,
- mMockMultiInstanceHelper);
+ mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
+ mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
+ new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
+ maximizeMenuFactory, mMockHandleMenuFactory, mMockMultiInstanceHelper);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
@@ -923,6 +952,13 @@
}
+ private void createHandleMenu(@NonNull DesktopModeWindowDecoration decor) {
+ decor.createHandleMenu();
+ // Call DesktopModeWindowDecoration#onAssistContentReceived because decor waits to receive
+ // {@link AssistContent} before creating the menu
+ decor.onAssistContentReceived(mAssistContent);
+ }
+
private static boolean hasNoInputChannelFeature(RelayoutParams params) {
return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL)
!= 0;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 627dfe7..100abbb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -33,6 +33,7 @@
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
import android.view.View
+import android.view.WindowManager
import androidx.core.graphics.toPointF
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags
@@ -77,6 +78,8 @@
@Mock
private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
@Mock
+ private lateinit var mockWindowManager: WindowManager
+ @Mock
private lateinit var onClickListener: View.OnClickListener
@Mock
private lateinit var onTouchListener: View.OnTouchListener
@@ -230,8 +233,9 @@
}
else -> error("Invalid windowing mode")
}
- val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId, appIcon, appName,
- splitScreenController, shouldShowWindowingPill = true,
+ val handleMenu = HandleMenu(mockDesktopWindowDecoration,
+ WindowManagerWrapper(mockWindowManager),
+ layoutId, appIcon, appName, splitScreenController, shouldShowWindowingPill = true,
shouldShowNewWindowButton = true,
null /* openInBrowserLink */, captionWidth = HANDLE_WIDTH, captionHeight = 50,
captionX = captionX
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index a07be79..e0d16aa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -97,7 +97,7 @@
.thenReturn(spyResizeVeilSurfaceBuilder)
doReturn(mockResizeVeilSurface).whenever(spyResizeVeilSurfaceBuilder).build()
whenever(mockSurfaceControlBuilderFactory
- .create(eq("Resize veil background of Task=" + taskInfo.taskId), any()))
+ .create(eq("Resize veil background of Task=" + taskInfo.taskId)))
.thenReturn(spyBackgroundSurfaceBuilder)
doReturn(mockBackgroundSurface).whenever(spyBackgroundSurfaceBuilder).build()
whenever(mockSurfaceControlBuilderFactory
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
index 28b4eb6..0f52ed7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
@@ -25,6 +25,7 @@
import androidx.test.filters.SmallTest
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,6 +71,7 @@
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
viewContainer = AdditionalSystemViewContainer(
mockContext,
+ WindowManagerWrapper(mockWindowManager),
TASK_ID,
X,
Y,
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 43a70c1..5a4cff0 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -309,9 +309,8 @@
return NULL;
}
- // We succeeded, so relinquish control of dataMap
pAsset->mAccessMode = mode;
- return std::move(pAsset);
+ return pAsset;
}
/*
@@ -328,9 +327,8 @@
return NULL;
}
- // We succeeded, so relinquish control of dataMap
pAsset->mAccessMode = mode;
- return std::move(pAsset);
+ return pAsset;
}
/*
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index 09232b6..a58493a 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -7,6 +7,17 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+cc_library_headers {
+ name: "libhostgraphics_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
cc_library_host_static {
name: "libhostgraphics",
@@ -30,12 +41,13 @@
],
header_libs: [
+ "libhostgraphics_headers",
"libnativebase_headers",
"libnativedisplay_headers",
"libnativewindow_headers",
],
- export_include_dirs: ["."],
+ export_include_dirs: ["include"],
target: {
windows: {
diff --git a/libs/hostgraphics/gui/BufferItem.h b/libs/hostgraphics/include/gui/BufferItem.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferItem.h
rename to libs/hostgraphics/include/gui/BufferItem.h
diff --git a/libs/hostgraphics/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferItemConsumer.h
rename to libs/hostgraphics/include/gui/BufferItemConsumer.h
diff --git a/libs/hostgraphics/gui/BufferQueue.h b/libs/hostgraphics/include/gui/BufferQueue.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferQueue.h
rename to libs/hostgraphics/include/gui/BufferQueue.h
diff --git a/libs/hostgraphics/gui/ConsumerBase.h b/libs/hostgraphics/include/gui/ConsumerBase.h
similarity index 100%
rename from libs/hostgraphics/gui/ConsumerBase.h
rename to libs/hostgraphics/include/gui/ConsumerBase.h
diff --git a/libs/hostgraphics/gui/IGraphicBufferConsumer.h b/libs/hostgraphics/include/gui/IGraphicBufferConsumer.h
similarity index 100%
rename from libs/hostgraphics/gui/IGraphicBufferConsumer.h
rename to libs/hostgraphics/include/gui/IGraphicBufferConsumer.h
diff --git a/libs/hostgraphics/gui/IGraphicBufferProducer.h b/libs/hostgraphics/include/gui/IGraphicBufferProducer.h
similarity index 100%
rename from libs/hostgraphics/gui/IGraphicBufferProducer.h
rename to libs/hostgraphics/include/gui/IGraphicBufferProducer.h
diff --git a/libs/hostgraphics/gui/Surface.h b/libs/hostgraphics/include/gui/Surface.h
similarity index 100%
rename from libs/hostgraphics/gui/Surface.h
rename to libs/hostgraphics/include/gui/Surface.h
diff --git a/libs/hostgraphics/ui/Fence.h b/libs/hostgraphics/include/ui/Fence.h
similarity index 100%
rename from libs/hostgraphics/ui/Fence.h
rename to libs/hostgraphics/include/ui/Fence.h
diff --git a/libs/hostgraphics/ui/GraphicBuffer.h b/libs/hostgraphics/include/ui/GraphicBuffer.h
similarity index 100%
rename from libs/hostgraphics/ui/GraphicBuffer.h
rename to libs/hostgraphics/include/ui/GraphicBuffer.h
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/FeatureFlags.h b/libs/hwui/FeatureFlags.h
index c1c30f5..fddcf29 100644
--- a/libs/hwui/FeatureFlags.h
+++ b/libs/hwui/FeatureFlags.h
@@ -25,22 +25,6 @@
namespace text_feature {
-inline bool fix_double_underline() {
-#ifdef __ANDROID__
- return com_android_text_flags_fix_double_underline();
-#else
- return true;
-#endif // __ANDROID__
-}
-
-inline bool deprecate_ui_fonts() {
-#ifdef __ANDROID__
- return com_android_text_flags_deprecate_ui_fonts();
-#else
- return true;
-#endif // __ANDROID__
-}
-
inline bool letter_spacing_justification() {
#ifdef __ANDROID__
return com_android_text_flags_letter_spacing_justification();
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 27ea150..236c3736 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>
@@ -318,6 +318,11 @@
return has101012Support;
}
+bool HardwareBitmapUploader::has10101010Support() {
+ static bool has1010110Support = checkSupport(AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM);
+ return has1010110Support;
+}
+
bool HardwareBitmapUploader::hasAlpha8Support() {
static bool hasAlpha8Support = checkSupport(AHARDWAREBUFFER_FORMAT_R8_UNORM);
return hasAlpha8Support;
@@ -376,6 +381,19 @@
}
formatInfo.format = GL_RGBA;
break;
+ case kRGBA_10x6_SkColorType:
+ formatInfo.isSupported = HardwareBitmapUploader::has10101010Support();
+ if (formatInfo.isSupported) {
+ formatInfo.type = 0; // Not supported in GL
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16;
+ } else {
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ }
+ formatInfo.format = 0; // Not supported in GL
+ break;
case kAlpha_8_SkColorType:
formatInfo.isSupported = HardwareBitmapUploader::hasAlpha8Support();
if (formatInfo.isSupported) {
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index 00ee996..76cb80b 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -33,12 +33,14 @@
#ifdef __ANDROID__
static bool hasFP16Support();
static bool has1010102Support();
+ static bool has10101010Support();
static bool hasAlpha8Support();
#else
static bool hasFP16Support() {
return true;
}
static bool has1010102Support() { return true; }
+ static bool has10101010Support() { return true; }
static bool hasAlpha8Support() { return true; }
#endif
};
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/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 72e83af..9e825fb 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -841,9 +841,6 @@
sk_sp<SkTextBlob> textBlob(builder.make());
applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); });
- if (!text_feature::fix_double_underline()) {
- drawTextDecorations(x, y, totalAdvance, paintCopy);
- }
}
void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 80b6c03..5af4af2 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -110,28 +110,26 @@
DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
- if (text_feature::fix_double_underline()) {
- Paint copied(paint);
- PaintFilter* filter = getPaintFilter();
- if (filter != nullptr) {
- filter->filterFullPaint(&copied);
+ Paint copied(paint);
+ PaintFilter* filter = getPaintFilter();
+ if (filter != nullptr) {
+ filter->filterFullPaint(&copied);
+ }
+ const bool isUnderline = copied.isUnderline();
+ const bool isStrikeThru = copied.isStrikeThru();
+ if (isUnderline || isStrikeThru) {
+ const SkScalar left = x;
+ const SkScalar right = x + layout.getAdvance();
+ if (isUnderline) {
+ const SkScalar top = y + f.getUnderlinePosition();
+ drawStroke(left, right, top, f.getUnderlineThickness(), copied, this);
}
- const bool isUnderline = copied.isUnderline();
- const bool isStrikeThru = copied.isStrikeThru();
- if (isUnderline || isStrikeThru) {
- const SkScalar left = x;
- const SkScalar right = x + layout.getAdvance();
- if (isUnderline) {
- const SkScalar top = y + f.getUnderlinePosition();
- drawStroke(left, right, top, f.getUnderlineThickness(), copied, this);
- }
- if (isStrikeThru) {
- float textSize = paint.getSkFont().getSize();
- const float position = textSize * Paint::kStdStrikeThru_Top;
- const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness;
- const SkScalar top = y + position;
- drawStroke(left, right, top, thickness, copied, this);
- }
+ if (isStrikeThru) {
+ float textSize = paint.getSkFont().getSize();
+ const float position = textSize * Paint::kStdStrikeThru_Top;
+ const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness;
+ const SkScalar top = y + position;
+ drawStroke(left, right, top, thickness, copied, this);
}
}
}
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 4eb6918..b6988b2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -28,11 +28,9 @@
#include "pipeline/skia/AnimatedDrawables.h"
#include "utils/Macros.h"
-class SkAnimatedImage;
enum class SkBlendMode;
class SkCanvasState;
class SkRRect;
-class SkRuntimeShaderBuilder;
class SkVertices;
namespace minikin {
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 0efb2c8..d7bf201 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -142,32 +142,30 @@
canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
}
- if (text_feature::fix_double_underline()) {
- // Extract underline position and thickness.
- if (paint.isUnderline()) {
- SkFontMetrics metrics;
- paint.getSkFont().getMetrics(&metrics);
- const float textSize = paint.getSkFont().getSize();
- SkScalar position;
- if (!metrics.hasUnderlinePosition(&position)) {
- position = textSize * Paint::kStdUnderline_Top;
- }
- SkScalar thickness;
- if (!metrics.hasUnderlineThickness(&thickness)) {
- thickness = textSize * Paint::kStdUnderline_Thickness;
- }
-
- // If multiple fonts are used, use the most bottom position and most thick stroke
- // width as the underline position. This follows the CSS standard:
- // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property
- // <quote>
- // The exact position and thickness of line decorations is UA-defined in this level.
- // However, for underlines and overlines the UA must use a single thickness and
- // position on each line for the decorations deriving from a single decorating box.
- // </quote>
- underlinePosition = std::max(underlinePosition, position);
- underlineThickness = std::max(underlineThickness, thickness);
+ // Extract underline position and thickness.
+ if (paint.isUnderline()) {
+ SkFontMetrics metrics;
+ paint.getSkFont().getMetrics(&metrics);
+ const float textSize = paint.getSkFont().getSize();
+ SkScalar position;
+ if (!metrics.hasUnderlinePosition(&position)) {
+ position = textSize * Paint::kStdUnderline_Top;
}
+ SkScalar thickness;
+ if (!metrics.hasUnderlineThickness(&thickness)) {
+ thickness = textSize * Paint::kStdUnderline_Thickness;
+ }
+
+ // If multiple fonts are used, use the most bottom position and most thick stroke
+ // width as the underline position. This follows the CSS standard:
+ // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property
+ // <quote>
+ // The exact position and thickness of line decorations is UA-defined in this level.
+ // However, for underlines and overlines the UA must use a single thickness and
+ // position on each line for the decorations deriving from a single decorating box.
+ // </quote>
+ underlinePosition = std::max(underlinePosition, position);
+ underlineThickness = std::max(underlineThickness, thickness);
}
}
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index d66d7f8..ede385a 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -53,9 +53,7 @@
if (familyVariant.has_value()) {
minikinPaint.familyVariant = familyVariant.value();
} else {
- minikinPaint.familyVariant = text_feature::deprecate_ui_fonts()
- ? minikin::FamilyVariant::ELEGANT
- : minikin::FamilyVariant::DEFAULT;
+ minikinPaint.familyVariant = minikin::FamilyVariant::ELEGANT;
}
return minikinPaint;
}
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 e4b1f916..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/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index dfda25d..8ec0430 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -654,6 +654,9 @@
if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
const auto inputEventId =
static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
+ ATRACE_FORMAT(
+ "frameTimelineInfo(frameNumber=%llu, vsyncId=%lld, inputEventId=0x%" PRIx32 ")",
+ frameCompleteNr, vsyncId, inputEventId);
const ANativeWindowFrameTimelineInfo ftl = {
.frameNumber = frameCompleteNr,
.frameTimelineVsyncId = vsyncId,
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..e302393 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>
@@ -318,6 +318,15 @@
tailPNext = &deviceFaultFeatures->pNext;
}
+ if (grExtensions.hasExtension(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME, 1)) {
+ VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT* formatFeatures =
+ new VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT;
+ formatFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT;
+ formatFeatures->pNext = nullptr;
+ *tailPNext = formatFeatures;
+ tailPNext = &formatFeatures->pNext;
+ }
+
// query to get the physical device features
mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
// this looks like it would slow things down,
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/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp
index c70a304..ecb06d8 100644
--- a/libs/hwui/tests/unit/UnderlineTest.cpp
+++ b/libs/hwui/tests/unit/UnderlineTest.cpp
@@ -109,9 +109,7 @@
return f;
}
-TEST_WITH_FLAGS(UnderlineTest, Roboto,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
- fix_double_underline))) {
+TEST(UnderlineTest, Roboto) {
float textSize = 100;
Paint paint;
paint.getSkFont().setSize(textSize);
@@ -123,9 +121,7 @@
EXPECT_EQ(ROBOTO_THICKNESS_EM * textSize, functor.getUnderlineThickness());
}
-TEST_WITH_FLAGS(UnderlineTest, NotoCJK,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
- fix_double_underline))) {
+TEST(UnderlineTest, NotoCJK) {
float textSize = 100;
Paint paint;
paint.getSkFont().setSize(textSize);
@@ -137,9 +133,7 @@
EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness());
}
-TEST_WITH_FLAGS(UnderlineTest, Mixture,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
- fix_double_underline))) {
+TEST(UnderlineTest, Mixture) {
float textSize = 100;
Paint paint;
paint.getSkFont().setSize(textSize);
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 6a560b3..9673c5f 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -49,6 +49,10 @@
colorType = kRGBA_1010102_SkColorType;
alphaType = kPremul_SkAlphaType;
break;
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
+ colorType = kRGBA_10x6_SkColorType;
+ alphaType = kPremul_SkAlphaType;
+ break;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
colorType = kRGBA_F16_SkColorType;
alphaType = kPremul_SkAlphaType;
@@ -86,6 +90,8 @@
return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
case kRGBA_1010102_SkColorType:
return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
+ case kRGBA_10x6_SkColorType:
+ return AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
case kARGB_4444_SkColorType:
// Hardcoding the value from android::PixelFormat
static constexpr uint64_t kRGBA4444 = 7;
@@ -108,6 +114,8 @@
return kRGB_565_SkColorType;
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
return kRGBA_1010102_SkColorType;
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
+ return kRGBA_10x6_SkColorType;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
return kRGBA_F16_SkColorType;
case AHARDWAREBUFFER_FORMAT_R8_UNORM:
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index eecc741..1afef75 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -25,6 +25,9 @@
#include <input/Input.h>
#include <log/log.h>
+#define INDENT " "
+#define INDENT2 " "
+
namespace {
// Time to spend fading out the pointer completely.
const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
@@ -449,6 +452,24 @@
return mLocked.resourcesLoaded;
}
+std::string MouseCursorController::dump() const {
+ std::string dump = INDENT "MouseCursorController:\n";
+ std::scoped_lock lock(mLock);
+ dump += StringPrintf(INDENT2 "viewport: %s\n", mLocked.viewport.toString().c_str());
+ dump += StringPrintf(INDENT2 "stylusHoverMode: %s\n",
+ mLocked.stylusHoverMode ? "true" : "false");
+ dump += StringPrintf(INDENT2 "pointerFadeDirection: %d\n", mLocked.pointerFadeDirection);
+ dump += StringPrintf(INDENT2 "updatePointerIcon: %s\n",
+ mLocked.updatePointerIcon ? "true" : "false");
+ dump += StringPrintf(INDENT2 "resourcesLoaded: %s\n",
+ mLocked.resourcesLoaded ? "true" : "false");
+ dump += StringPrintf(INDENT2 "requestedPointerType: %d\n", mLocked.requestedPointerType);
+ dump += StringPrintf(INDENT2 "resolvedPointerType: %d\n", mLocked.resolvedPointerType);
+ dump += StringPrintf(INDENT2 "skipScreenshot: %s\n", mLocked.skipScreenshot ? "true" : "false");
+ dump += StringPrintf(INDENT2 "animating: %s\n", mLocked.animating ? "true" : "false");
+ return dump;
+}
+
bool MouseCursorController::doAnimations(nsecs_t timestamp) {
std::scoped_lock lock(mLock);
bool keepFading = doFadingAnimationLocked(timestamp);
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index 78f6413..8600341 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -67,6 +67,8 @@
bool resourcesLoaded();
+ std::string dump() const;
+
private:
mutable std::mutex mLock;
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 11b27a2..5ae967b 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -25,6 +25,7 @@
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <ftl/enum.h>
+#include <input/PrintTools.h>
#include <mutex>
@@ -353,6 +354,8 @@
for (const auto& [_, spotController] : mLocked.spotControllers) {
spotController.dump(dump, INDENT3);
}
+ dump += INDENT2 "Cursor Controller:\n";
+ dump += addLinePrefix(mCursorController.dump(), INDENT3);
return dump;
}
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index f6e76a2..cf3f740 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -437,7 +437,7 @@
}
public class LocationManager {
- method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
+ method @Deprecated @FlaggedApi("android.location.flags.deprecate_provider_request_apis") @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @Nullable public String getExtraLocationControllerPackage();
@@ -452,7 +452,7 @@
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
- method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void removeProviderRequestChangedListener(@NonNull android.location.provider.ProviderRequest.ChangedListener);
+ method @Deprecated @FlaggedApi("android.location.flags.deprecate_provider_request_apis") @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void removeProviderRequestChangedListener(@NonNull android.location.provider.ProviderRequest.ChangedListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d921730..6342489 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -28,6 +28,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -47,6 +48,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.location.flags.Flags;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
@@ -2957,8 +2959,13 @@
*
* @param executor the executor that the callback runs on
* @param listener the listener to register
+ *
+ * @deprecated Do not use - this API was intended for testing only, and may not return any
+ * results in the future.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_PROVIDER_REQUEST_APIS)
+ @Deprecated
@SystemApi
@RequiresPermission(allOf = {Manifest.permission.LOCATION_HARDWARE,
Manifest.permission.INTERACT_ACROSS_USERS})
@@ -2973,8 +2980,13 @@
* Removes a {@link ProviderRequest.ChangedListener} that has been added.
*
* @param listener the listener to remove.
+ *
+ * @deprecated Do not use - this API was intended for testing only, and may not return any
+ * results in the future.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_PROVIDER_REQUEST_APIS)
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public void removeProviderRequestChangedListener(
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 0edaaef..bbd91fc 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -2,6 +2,13 @@
container: "system"
flag {
+ name: "deprecate_provider_request_apis"
+ namespace: "location"
+ description: "Deprecates LocationManager ProviderChanged APIs"
+ bug: "361811782"
+}
+
+flag {
name: "keep_gnss_stationary_throttling"
namespace: "location"
description: "Keeps stationary throttling for the GNSS provider even if the disable_stationary_throttling flag is true."
@@ -102,3 +109,11 @@
description: "Flag for GNSS configuration from resource"
bug: "317734846"
}
+
+flag {
+ name: "enable_ni_supl_message_injection_by_carrier_config"
+ namespace: "location"
+ description: "Flag for enabling NI SUPL message injection by carrier config"
+ bug: "242105192"
+ is_fixed_read_only: true
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 25fae76..4981cb3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6984,6 +6984,27 @@
}
/**
+ * Test method for enabling/disabling the volume controller long press timeout for checking
+ * whether two consecutive volume adjustments should be treated as a volume long press.
+ *
+ * <p>Used only for testing
+ *
+ * @param enable true for enabling, otherwise will be disabled (test mode)
+ *
+ * @hide
+ **/
+ @TestApi
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public void setVolumeControllerLongPressTimeoutEnabled(boolean enable) {
+ try {
+ getService().setVolumeControllerLongPressTimeoutEnabled(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Only useful for volume controllers.
* @hide
*/
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index a255f73..ebdfd3e 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2655,7 +2655,16 @@
/**
* Register a native listener for system property sysprop
* @param callback the listener which fires when the property changes
+ * @return a native handle for use in subsequent methods
* @hide
*/
- public static native void listenForSystemPropertyChange(String sysprop, Runnable callback);
+ public static native long listenForSystemPropertyChange(String sysprop, Runnable callback);
+
+ /**
+ * Trigger a sysprop listener update, if the property has been updated: synchronously validating
+ * there are no pending sysprop changes.
+ * @param handle the handle returned by {@link listenForSystemPropertyChange}
+ * @hide
+ */
+ public static native void triggerSystemPropertyUpdate(long handle);
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index a96562d..9af6b28 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -303,6 +303,9 @@
void notifyVolumeControllerVisible(in IVolumeController controller, boolean visible);
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ oneway void setVolumeControllerLongPressTimeoutEnabled(boolean enable);
+
boolean isStreamAffectedByRingerMode(int streamType);
boolean isStreamAffectedByMute(int streamType);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 1930c3d..b84990b 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -288,8 +288,7 @@
/**
* Returns a proxy MediaRouter2 instance that allows you to control the routing of an app
- * specified by {@code clientPackageName}. Returns {@code null} if the specified package name
- * does not exist.
+ * specified by {@code clientPackageName}.
*
* <p>Proxy MediaRouter2 instances operate differently than regular MediaRouter2 instances:
*
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/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 93bca21..c814c95 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -48,3 +48,11 @@
description: "Tuner V4.0 APIs for Android W"
bug: "320419647"
}
+
+flag {
+ name: "hdmi_control_enhanced_behavior"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Enhance HDMI-CEC power state and activeness transitions"
+ bug: "332780751"
+}
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index a991a71f..7acb8c7 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -74,8 +74,4 @@
symbol_file: "libamidi.map.txt",
first_version: "29",
- export_header_libs: [
- "amidi",
- ],
-
}
diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp
index 3dc2a0a..43b1a35 100644
--- a/media/tests/AudioPolicyTest/Android.bp
+++ b/media/tests/AudioPolicyTest/Android.bp
@@ -24,3 +24,11 @@
resource_dirs: ["res"],
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "audiopolicytest_audiopolicytest_audiopolicydeathtest_Presubmit",
+ base: "audiopolicytest",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.audiopolicytest.AudioPolicyDeathTest"],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index c4c4102..3eb99c3 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -27,9 +27,6 @@
symbol_file: "libandroid.map.txt",
first_version: "9",
unversioned_until: "current",
- export_header_libs: [
- "libandroid_headers",
- ],
}
cc_defaults {
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 717e01e..cc5ff81 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -73,6 +73,7 @@
method public void onApplyRouting(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onBootFinished(int);
method public void onBootStarted();
+ method public void onCardEmulationActivated(boolean);
method public void onDisable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onDisableFinished(int);
method public void onDisableStarted();
@@ -81,6 +82,9 @@
method public void onEnableStarted();
method public void onHceEventReceived(int);
method public void onNdefRead(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method public void onReaderOptionChanged(boolean);
+ method public void onRfDiscoveryStarted(boolean);
+ method public void onRfFieldActivated(boolean);
method public void onRoutingChanged();
method public void onStateUpdated(int);
method public void onTagConnected(boolean, @NonNull android.nfc.Tag);
@@ -94,6 +98,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/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 6c0f933..e2ec952 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -62,7 +62,7 @@
void dispatch(in Tag tag);
- void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
+ void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras, String pkg);
void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
@@ -100,7 +100,7 @@
void unregisterWlcStateListener(in INfcWlcStateListener listener);
WlcListenerDeviceInfo getWlcListenerDeviceInfo();
- void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
+ void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags, String pkg);
void notifyPollingLoop(in PollingFrame frame);
void notifyHceDeactivated();
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/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index c19a44b..e49ef7e 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -37,4 +37,8 @@
void onTagDispatch(in ResultReceiver isSkipped);
void onRoutingChanged();
void onHceEventReceived(int action);
+ void onReaderOptionChanged(boolean enabled);
+ void onCardEmulationActivated(boolean isActivated);
+ void onRfFieldActivated(boolean isActivated);
+ void onRfDiscoveryStarted(boolean isDiscoveryStarted);
}
diff --git a/nfc/java/android/nfc/NfcActivityManager.java b/nfc/java/android/nfc/NfcActivityManager.java
index 0eb846d..909eca7 100644
--- a/nfc/java/android/nfc/NfcActivityManager.java
+++ b/nfc/java/android/nfc/NfcActivityManager.java
@@ -236,7 +236,8 @@
public void setReaderMode(Binder token, int flags, Bundle extras) {
if (DBG) Log.d(TAG, "Setting reader mode");
- NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode(token, this, flags, extras));
+ NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode(
+ token, this, flags, extras, mAdapter.getContext().getPackageName()));
}
/**
@@ -395,7 +396,8 @@
private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) {
NfcAdapter.callService(
- () -> NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech));
+ () -> NfcAdapter.sService.updateDiscoveryTechnology(
+ token, pollTech, listenTech, mAdapter.getContext().getPackageName()));
}
}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 525e2c5..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;
}
@@ -1731,7 +1736,8 @@
}
Binder token = new Binder();
int flags = enable ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
- callService(() -> sService.setReaderMode(token, null, flags, null));
+ callService(() -> sService.setReaderMode(
+ token, null, flags, null, mContext.getPackageName()));
}
/**
@@ -1804,7 +1810,8 @@
|| (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) {
Binder token = new Binder();
callService( () ->
- sService.updateDiscoveryTechnology(token, pollTechnology, listenTechnology));
+ sService.updateDiscoveryTechnology(
+ token, pollTechnology, listenTechnology, mContext.getPackageName()));
} else {
mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology);
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 6c02edd..d51b704 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -32,7 +32,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
@@ -40,6 +42,7 @@
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -58,10 +61,13 @@
private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000;
private final NfcAdapter mAdapter;
private final NfcOemExtensionCallback mOemNfcExtensionCallback;
+ private boolean mIsRegistered = false;
+ private final Map<Callback, Executor> mCallbackMap = new HashMap<>();
private final Context mContext;
- private Executor mExecutor = null;
- private Callback mCallback = null;
private final Object mLock = new Object();
+ private boolean mCardEmulationActivated = false;
+ private boolean mRfFieldActivated = false;
+ private boolean mRfDiscoveryStarted = false;
/**
* Event that Host Card Emulation is activated.
@@ -215,6 +221,39 @@
* @param action Flag indicating actions to activate, start and stop cpu boost.
*/
void onHceEventReceived(@HostCardEmulationAction int action);
+
+ /**
+ * API to notify when reader option has been changed using
+ * {@link NfcAdapter#enableReaderOption(boolean)} by some app.
+ * @param enabled Flag indicating ReaderMode enabled/disabled
+ */
+ void onReaderOptionChanged(boolean enabled);
+
+ /**
+ * Notifies NFC is activated in listen mode.
+ * NFC Forum NCI-2.3 ch.5.2.6 specification
+ *
+ * <p>NFCC is ready to communicate with a Card reader
+ *
+ * @param isActivated true, if card emulation activated, else de-activated.
+ */
+ void onCardEmulationActivated(boolean isActivated);
+
+ /**
+ * Notifies the Remote NFC Endpoint RF Field is activated.
+ * NFC Forum NCI-2.3 ch.5.3 specification
+ *
+ * @param isActivated true, if RF Field is ON, else RF Field is OFF.
+ */
+ void onRfFieldActivated(boolean isActivated);
+
+ /**
+ * Notifies the NFC RF discovery is started or in the IDLE state.
+ * NFC Forum NCI-2.3 ch.5.2 specification
+ *
+ * @param isDiscoveryStarted true, if RF discovery started, else RF state is Idle.
+ */
+ void onRfDiscoveryStarted(boolean isDiscoveryStarted);
}
@@ -229,7 +268,12 @@
/**
* Register an {@link Callback} to listen for NFC oem extension callbacks
+ * Multiple clients can register and callbacks will be invoked asynchronously.
+ *
* <p>The provided callback will be invoked by the given {@link Executor}.
+ * As part of {@link #registerCallback(Executor, Callback)} the
+ * {@link Callback} will be invoked with current NFC state
+ * before the {@link #registerCallback(Executor, Callback)} function completes.
*
* @param executor an {@link Executor} to execute given callback
* @param callback oem implementation of {@link Callback}
@@ -239,15 +283,35 @@
public void registerCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull Callback callback) {
synchronized (mLock) {
- if (mCallback != null) {
+ if (executor == null || callback == null) {
+ Log.e(TAG, "Executor and Callback must not be null!");
+ throw new IllegalArgumentException();
+ }
+
+ if (mCallbackMap.containsKey(callback)) {
Log.e(TAG, "Callback already registered. Unregister existing callback before"
+ "registering");
throw new IllegalArgumentException();
}
- NfcAdapter.callService(() -> {
- NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
- mCallback = callback;
- mExecutor = executor;
+ mCallbackMap.put(callback, executor);
+ if (!mIsRegistered) {
+ NfcAdapter.callService(() -> {
+ NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
+ mIsRegistered = true;
+ });
+ } else {
+ updateNfCState(callback, executor);
+ }
+ }
+ }
+
+ private void updateNfCState(Callback callback, Executor executor) {
+ if (callback != null) {
+ Log.i(TAG, "updateNfCState");
+ executor.execute(() -> {
+ callback.onCardEmulationActivated(mCardEmulationActivated);
+ callback.onRfFieldActivated(mRfFieldActivated);
+ callback.onRfDiscoveryStarted(mRfDiscoveryStarted);
});
}
}
@@ -266,15 +330,19 @@
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public void unregisterCallback(@NonNull Callback callback) {
synchronized (mLock) {
- if (mCallback == null || mCallback != callback) {
+ if (!mCallbackMap.containsKey(callback) || !mIsRegistered) {
Log.e(TAG, "Callback not registered");
throw new IllegalArgumentException();
}
- NfcAdapter.callService(() -> {
- NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
- mCallback = null;
- mExecutor = null;
- });
+ if (mCallbackMap.size() == 1) {
+ NfcAdapter.callService(() -> {
+ NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
+ mIsRegistered = false;
+ mCallbackMap.remove(callback);
+ });
+ } else {
+ mCallbackMap.remove(callback);
+ }
}
}
@@ -322,90 +390,139 @@
}
private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
+
@Override
public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoid2ArgCallback(connected, tag, cb::onTagConnected, ex));
+ }
+
+ @Override
+ public void onCardEmulationActivated(boolean isActivated) throws RemoteException {
+ mCardEmulationActivated = isActivated;
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(isActivated, cb::onCardEmulationActivated, ex));
+ }
+
+ @Override
+ public void onRfFieldActivated(boolean isActivated) throws RemoteException {
+ mRfFieldActivated = isActivated;
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(isActivated, cb::onRfFieldActivated, ex));
+ }
+
+ @Override
+ public void onRfDiscoveryStarted(boolean isDiscoveryStarted) throws RemoteException {
+ mRfDiscoveryStarted = isDiscoveryStarted;
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(isDiscoveryStarted, cb::onRfDiscoveryStarted, ex));
+ }
+
+ @Override
+ public void onStateUpdated(int state) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(state, cb::onStateUpdated, ex));
+ }
+
+ @Override
+ public void onApplyRouting(ResultReceiver isSkipped) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isSkipped), cb::onApplyRouting, ex));
+ }
+ @Override
+ public void onNdefRead(ResultReceiver isSkipped) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isSkipped), cb::onNdefRead, ex));
+ }
+ @Override
+ public void onEnable(ResultReceiver isAllowed) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isAllowed), cb::onEnable, ex));
+ }
+ @Override
+ public void onDisable(ResultReceiver isAllowed) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isAllowed), cb::onDisable, ex));
+ }
+ @Override
+ public void onBootStarted() throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onBootStarted(), ex));
+ }
+ @Override
+ public void onEnableStarted() throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onEnableStarted(), ex));
+ }
+ @Override
+ public void onDisableStarted() throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onDisableStarted(), ex));
+ }
+ @Override
+ public void onBootFinished(int status) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(status, cb::onBootFinished, ex));
+ }
+ @Override
+ public void onEnableFinished(int status) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(status, cb::onEnableFinished, ex));
+ }
+ @Override
+ public void onDisableFinished(int status) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(status, cb::onDisableFinished, ex));
+ }
+ @Override
+ public void onTagDispatch(ResultReceiver isSkipped) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isSkipped), cb::onTagDispatch, ex));
+ }
+ @Override
+ public void onRoutingChanged() throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onRoutingChanged(), ex));
+ }
+ @Override
+ public void onHceEventReceived(int action) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(action, cb::onHceEventReceived, ex));
+ }
+
+ @Override
+ public void onReaderOptionChanged(boolean enabled) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(enabled, cb::onReaderOptionChanged, ex));
+ }
+
+ private <T> void handleVoidCallback(
+ T input, Consumer<T> callbackMethod, Executor executor) {
synchronized (mLock) {
- if (mCallback == null || mExecutor == null) {
- return;
- }
final long identity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> mCallback.onTagConnected(connected, tag));
+ executor.execute(() -> callbackMethod.accept(input));
+ } catch (RuntimeException ex) {
+ throw ex;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
- @Override
- public void onStateUpdated(int state) throws RemoteException {
- handleVoidCallback(state, mCallback::onStateUpdated);
- }
- @Override
- public void onApplyRouting(ResultReceiver isSkipped) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isSkipped), mCallback::onApplyRouting);
- }
- @Override
- public void onNdefRead(ResultReceiver isSkipped) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isSkipped), mCallback::onNdefRead);
- }
- @Override
- public void onEnable(ResultReceiver isAllowed) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isAllowed), mCallback::onEnable);
- }
- @Override
- public void onDisable(ResultReceiver isAllowed) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isAllowed), mCallback::onDisable);
- }
- @Override
- public void onBootStarted() throws RemoteException {
- handleVoidCallback(null, (Object input) -> mCallback.onBootStarted());
- }
- @Override
- public void onEnableStarted() throws RemoteException {
- handleVoidCallback(null, (Object input) -> mCallback.onEnableStarted());
- }
- @Override
- public void onDisableStarted() throws RemoteException {
- handleVoidCallback(null, (Object input) -> mCallback.onDisableStarted());
- }
- @Override
- public void onBootFinished(int status) throws RemoteException {
- handleVoidCallback(status, mCallback::onBootFinished);
- }
- @Override
- public void onEnableFinished(int status) throws RemoteException {
- handleVoidCallback(status, mCallback::onEnableFinished);
- }
- @Override
- public void onDisableFinished(int status) throws RemoteException {
- handleVoidCallback(status, mCallback::onDisableFinished);
- }
- @Override
- public void onTagDispatch(ResultReceiver isSkipped) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isSkipped), mCallback::onTagDispatch);
- }
- @Override
- public void onRoutingChanged() throws RemoteException {
- handleVoidCallback(null, (Object input) -> mCallback.onRoutingChanged());
- }
- @Override
- public void onHceEventReceived(int action) throws RemoteException {
- handleVoidCallback(action, mCallback::onHceEventReceived);
- }
- private <T> void handleVoidCallback(T input, Consumer<T> callbackMethod) {
+ private <T1, T2> void handleVoid2ArgCallback(
+ T1 input1, T2 input2, BiConsumer<T1, T2> callbackMethod, Executor executor) {
synchronized (mLock) {
- if (mCallback == null || mExecutor == null) {
- return;
- }
final long identity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> callbackMethod.accept(input));
+ executor.execute(() -> callbackMethod.accept(input1, input2));
+ } catch (RuntimeException ex) {
+ throw ex;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -415,17 +532,12 @@
private <S, T> S handleNonVoidCallbackWithInput(
S defaultValue, T input, Function<T, S> callbackMethod) throws RemoteException {
synchronized (mLock) {
- if (mCallback == null) {
- return defaultValue;
- }
final long identity = Binder.clearCallingIdentity();
S result = defaultValue;
try {
ExecutorService executor = Executors.newSingleThreadExecutor();
- FutureTask<S> futureTask = new FutureTask<>(
- () -> callbackMethod.apply(input)
- );
- executor.submit(futureTask);
+ FutureTask<S> futureTask = new FutureTask<>(() -> callbackMethod.apply(input));
+ var unused = executor.submit(futureTask);
try {
result = futureTask.get(
OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
@@ -447,17 +559,12 @@
private <T> T handleNonVoidCallbackWithoutInput(T defaultValue, Supplier<T> callbackMethod)
throws RemoteException {
synchronized (mLock) {
- if (mCallback == null) {
- return defaultValue;
- }
final long identity = Binder.clearCallingIdentity();
T result = defaultValue;
try {
ExecutorService executor = Executors.newSingleThreadExecutor();
- FutureTask<T> futureTask = new FutureTask<>(
- callbackMethod::get
- );
- executor.submit(futureTask);
+ FutureTask<T> futureTask = new FutureTask<>(callbackMethod::get);
+ var unused = executor.submit(futureTask);
try {
result = futureTask.get(
OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 3cf0a4d..b28237c 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -69,6 +69,29 @@
private static final String TAG = "ApduServiceInfo";
/**
+ * Component level {@link android.content.pm.PackageManager.Property PackageManager
+ * .Property} for a system application to change its icon and label
+ * on the default applications page. This property should be added to an
+ * {@link HostApduService} declaration in the manifest.
+ *
+ * <p>For example:
+ * <pre>
+ * <service
+ * android:apduServiceBanner="@drawable/product_logo"
+ * android:label="@string/service_label">
+ * <property
+ * android:name="android.content.PROPERTY_WALLET_ICON_AND_LABEL_HOLDER"
+ * android:value="true"/>
+ * </service>
+ * </pre>
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(android.permission.flags.Flags.FLAG_WALLET_ROLE_ICON_PROPERTY_ENABLED)
+ public static final String PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL =
+ "android.nfc.cardemulation.PROPERTY_WALLET_PREFERRED_BANNER_AND_LABEL";
+
+ /**
* The service that implements this
*/
private final ResolveInfo mService;
@@ -308,6 +331,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 0fda91d..cc9a97c 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -141,3 +141,19 @@
description: "Enable override and recover routing table"
bug: "329043523"
}
+
+flag {
+ name: "nfc_watchdog"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable watchdog for the NFC system process"
+ bug: "362937338"
+}
+
+flag {
+ name: "enable_card_emulation_euicc"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable EUICC card emulation"
+ bug: "321314635"
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 3409c29..defbc11 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,8 +16,6 @@
package com.android.externalstorage;
-import static java.util.regex.Pattern.CASE_INSENSITIVE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
@@ -61,12 +59,15 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
-import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* Presents content of the shared (a.k.a. "external") storage.
@@ -89,12 +90,9 @@
private static final Uri BASE_URI =
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
- /**
- * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and
- * {@code /Android/sandbox/} along with all their subdirectories and content.
- */
- private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES =
- Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE);
+ private static final String PRIMARY_EMULATED_STORAGE_PATH = "/storage/emulated/";
+
+ private static final String STORAGE_PATH = "/storage/";
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
@@ -309,11 +307,70 @@
return false;
}
- final String path = getPathFromDocId(documentId);
- return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches();
+ try {
+ final RootInfo root = getRootFromDocId(documentId);
+ final String canonicalPath = getPathFromDocId(documentId);
+ return isRestrictedPath(root.rootId, canonicalPath);
+ } catch (Exception e) {
+ return true;
+ }
}
/**
+ * Based on the given root id and path, we restrict path access if file is Android/data or
+ * Android/obb or Android/sandbox or one of their subdirectories.
+ *
+ * @param canonicalPath of the file
+ * @return true if path is restricted
+ */
+ private boolean isRestrictedPath(String rootId, String canonicalPath) {
+ if (rootId == null || canonicalPath == null) {
+ return true;
+ }
+
+ final String rootPath;
+ if (rootId.equalsIgnoreCase(ROOT_ID_PRIMARY_EMULATED)) {
+ // Creates "/storage/emulated/<user-id>"
+ rootPath = PRIMARY_EMULATED_STORAGE_PATH + UserHandle.myUserId();
+ } else {
+ // Creates "/storage/<volume-uuid>"
+ rootPath = STORAGE_PATH + rootId;
+ }
+ List<java.nio.file.Path> restrictedPathList = Arrays.asList(
+ Paths.get(rootPath, "Android", "data"),
+ Paths.get(rootPath, "Android", "obb"),
+ Paths.get(rootPath, "Android", "sandbox"));
+ // We need to identify restricted parent paths which actually exist on the device
+ List<java.nio.file.Path> validRestrictedPathsToCheck = restrictedPathList.stream().filter(
+ Files::exists).collect(Collectors.toList());
+
+ boolean isRestricted = false;
+ java.nio.file.Path filePathToCheck = Paths.get(rootPath, canonicalPath);
+ try {
+ while (filePathToCheck != null) {
+ for (java.nio.file.Path restrictedPath : validRestrictedPathsToCheck) {
+ if (Files.isSameFile(restrictedPath, filePathToCheck)) {
+ isRestricted = true;
+ Log.v(TAG, "Restricting access for path: " + filePathToCheck);
+ break;
+ }
+ }
+ if (isRestricted) {
+ break;
+ }
+
+ filePathToCheck = filePathToCheck.getParent();
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error in checking file equality check.", e);
+ isRestricted = true;
+ }
+
+ return isRestricted;
+ }
+
+
+ /**
* Check that the directory is the root of storage or blocked file from tree.
* <p>
* Note, that this is different from hidden documents: blocked documents <b>WILL</b> appear
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index 81f7315..fe27cee 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -654,52 +654,49 @@
public void renderPage(int pageIndex, RenderSpec renderSpec,
OnPageContentAvailableCallback callback) {
- // First, check if we have a rendered page for this index.
- RenderedPage renderedPage = mPageContentCache.getRenderedPage(pageIndex);
- if (renderedPage != null && renderedPage.state == RenderedPage.STATE_RENDERED) {
- // If we have rendered page with same constraints - done.
- if (renderedPage.renderSpec.equals(renderSpec)) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Cache hit for page: " + pageIndex);
- }
-
- // Announce if needed.
- if (callback != null) {
- callback.onPageContentAvailable(renderedPage.content);
- }
- return;
- } else {
- // If the constraints changed, mark the page obsolete.
- renderedPage.state = RenderedPage.STATE_SCRAP;
- }
- }
-
- // Next, check if rendering this page is scheduled.
- RenderPageTask renderTask = mPageToRenderTaskMap.get(pageIndex);
- if (renderTask != null && !renderTask.isCancelled()) {
- // If not rendered and constraints same....
- if (renderTask.mRenderSpec.equals(renderSpec)) {
- if (renderTask.mCallback != null) {
- // If someone else is already waiting for this page - bad state.
- if (callback != null && renderTask.mCallback != callback) {
- throw new IllegalStateException("Page rendering not cancelled");
+ synchronized (mPageToRenderTaskMap) {
+ RenderedPage renderedPage = mPageContentCache.getRenderedPage(pageIndex);
+ if (renderedPage != null && renderedPage.state == RenderedPage.STATE_RENDERED) {
+ // If we have rendered page with same constraints - done.
+ if (renderedPage.renderSpec.equals(renderSpec)) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cache hit for page: " + pageIndex);
}
- } else {
- // No callback means we are preloading so just let the argument
- // callback be attached to our work in progress.
- renderTask.mCallback = callback;
- }
- return;
- } else {
- // If not rendered and constraints changed - cancel rendering.
- renderTask.cancel(true);
- }
- }
- // Oh well, we will have work to do...
- renderTask = new RenderPageTask(pageIndex, renderSpec, callback);
- mPageToRenderTaskMap.put(pageIndex, renderTask);
- renderTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
+ // Announce if needed.
+ if (callback != null) {
+ callback.onPageContentAvailable(renderedPage.content);
+ }
+ return;
+ } else {
+ // If the constraints changed, mark the page obsolete.
+ renderedPage.state = RenderedPage.STATE_SCRAP;
+ }
+ }
+
+ // Next, check if rendering this page is scheduled.
+ RenderPageTask renderTask = mPageToRenderTaskMap.get(pageIndex);
+ if (renderTask != null && !renderTask.isCancelled()) {
+ // If not rendered and constraints same....
+ if (renderTask.mRenderSpec.equals(renderSpec)) {
+ renderTask.mCallback = callback;
+ return;
+ } else {
+ // If not rendered and constraints changed - cancel rendering.
+ try {
+ renderTask.cancel(true);
+ mPageToRenderTaskMap.remove(pageIndex);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error cancelling RenderPageTask ", e);
+ }
+ }
+ }
+
+ // Oh well, we will have work to do...
+ renderTask = new RenderPageTask(pageIndex, renderSpec, callback);
+ mPageToRenderTaskMap.put(pageIndex, renderTask);
+ renderTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
+ }
}
public void cancelRendering(int pageIndex) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index ff09084..c4173ed 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -460,7 +460,7 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
event.startTracking();
return true;
}
@@ -479,7 +479,7 @@
return true;
}
- if (keyCode == KeyEvent.KEYCODE_BACK
+ if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
&& event.isTracking() && !event.isCanceled()) {
if (mPrintPreviewController != null && mPrintPreviewController.isOptionsOpened()
&& !hasErrors()) {
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 0cb85d8..b997c35 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -42,6 +42,8 @@
"SettingsLibIllustrationPreference",
"SettingsLibLayoutPreference",
"SettingsLibMainSwitchPreference",
+ "SettingsLibMetadata",
+ "SettingsLibPreference",
"SettingsLibProfileSelector",
"SettingsLibProgressBar",
"SettingsLibRestrictedLockUtils",
diff --git a/packages/SettingsLib/DataStore/Android.bp b/packages/SettingsLib/DataStore/Android.bp
index 86c8f0da..6840e10 100644
--- a/packages/SettingsLib/DataStore/Android.bp
+++ b/packages/SettingsLib/DataStore/Android.bp
@@ -17,6 +17,7 @@
"androidx.annotation_annotation",
"androidx.collection_collection-ktx",
"androidx.core_core-ktx",
+ "error_prone_annotations",
"guava",
],
kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
new file mode 100644
index 0000000..3d41337
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.datastore
+
+import android.content.SharedPreferences
+
+/** Interface of key-value store. */
+interface KeyValueStore : KeyedObservable<String> {
+
+ /** Returns if the storage contains persistent value of given key. */
+ fun contains(key: String): Boolean
+
+ /** Gets default value of given key. */
+ fun <T : Any> getDefaultValue(key: String, valueType: Class<T>): T? =
+ when (valueType) {
+ Boolean::class.javaObjectType -> false
+ Float::class.javaObjectType -> 0f
+ Int::class.javaObjectType -> 0
+ Long::class.javaObjectType -> 0
+ else -> null
+ }
+ as T?
+
+ /** Gets value of given key. */
+ fun <T : Any> getValue(key: String, valueType: Class<T>): T?
+
+ /**
+ * Sets value for given key.
+ *
+ * @param key key
+ * @param valueType value type
+ * @param value value to set, null means remove
+ */
+ fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?)
+}
+
+/** [SharedPreferences] based [KeyValueStore]. */
+interface SharedPreferencesKeyValueStore : KeyValueStore {
+
+ /** [SharedPreferences] of the key-value store. */
+ val sharedPreferences: SharedPreferences
+
+ override fun contains(key: String): Boolean = sharedPreferences.contains(key)
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
+ when (valueType) {
+ Boolean::class.javaObjectType -> sharedPreferences.getBoolean(key, false)
+ Float::class.javaObjectType -> sharedPreferences.getFloat(key, 0f)
+ Int::class.javaObjectType -> sharedPreferences.getInt(key, 0)
+ Long::class.javaObjectType -> sharedPreferences.getLong(key, 0)
+ String::class.javaObjectType -> sharedPreferences.getString(key, null)
+ Set::class.javaObjectType -> sharedPreferences.getStringSet(key, null)
+ else -> null
+ }
+ as T?
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value == null) {
+ sharedPreferences.edit().remove(key).apply()
+ return
+ }
+ val edit = sharedPreferences.edit()
+ when (valueType) {
+ Boolean::class.javaObjectType -> edit.putBoolean(key, value as Boolean)
+ Float::class.javaObjectType -> edit.putFloat(key, value as Float)
+ Int::class.javaObjectType -> edit.putInt(key, value as Int)
+ Long::class.javaObjectType -> edit.putLong(key, value as Long)
+ String::class.javaObjectType -> edit.putString(key, value as String)
+ Set::class.javaObjectType -> edit.putStringSet(key, value as Set<String>)
+ else -> {}
+ }
+ edit.apply()
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 4ce1d37..ec90317 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -19,6 +19,7 @@
import androidx.annotation.AnyThread
import androidx.annotation.GuardedBy
import androidx.collection.MutableScatterMap
+import com.google.errorprone.annotations.CanIgnoreReturnValue
import java.util.WeakHashMap
import java.util.concurrent.Executor
@@ -62,8 +63,9 @@
*
* @param observer observer to be notified
* @param executor executor to run the callback
+ * @return if the observer is newly added
*/
- fun addObserver(observer: KeyedObserver<K?>, executor: Executor)
+ @CanIgnoreReturnValue fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean
/**
* Adds an observer on given key.
@@ -73,14 +75,24 @@
* @param key key to observe
* @param observer observer to be notified
* @param executor executor to run the callback
+ * @return if the observer is newly added
*/
- fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor)
+ @CanIgnoreReturnValue
+ fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean
- /** Removes observer. */
- fun removeObserver(observer: KeyedObserver<K?>)
+ /**
+ * Removes observer.
+ *
+ * @return if the observer is found and removed
+ */
+ @CanIgnoreReturnValue fun removeObserver(observer: KeyedObserver<K?>): Boolean
- /** Removes observer on given key. */
- fun removeObserver(key: K, observer: KeyedObserver<K>)
+ /**
+ * Removes observer on given key.
+ *
+ * @return if the observer is found and removed
+ */
+ @CanIgnoreReturnValue fun removeObserver(key: K, observer: KeyedObserver<K>): Boolean
/**
* Notifies all observers that a change occurs.
@@ -111,14 +123,17 @@
@GuardedBy("itself")
private val keyedObservers = MutableScatterMap<K, WeakHashMap<KeyedObserver<K>, Executor>>()
- override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) {
+ @CanIgnoreReturnValue
+ override fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean {
val oldExecutor = synchronized(observers) { observers.put(observer, executor) }
if (oldExecutor != null && oldExecutor != executor) {
throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
}
+ return oldExecutor == null
}
- override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) {
+ @CanIgnoreReturnValue
+ override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean {
val oldExecutor =
synchronized(keyedObservers) {
keyedObservers.getOrPut(key) { WeakHashMap() }.put(observer, executor)
@@ -126,20 +141,23 @@
if (oldExecutor != null && oldExecutor != executor) {
throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
}
+ return oldExecutor == null
}
- override fun removeObserver(observer: KeyedObserver<K?>) {
- synchronized(observers) { observers.remove(observer) }
- }
+ @CanIgnoreReturnValue
+ override fun removeObserver(observer: KeyedObserver<K?>) =
+ synchronized(observers) { observers.remove(observer) } != null
- override fun removeObserver(key: K, observer: KeyedObserver<K>) {
+ @CanIgnoreReturnValue
+ override fun removeObserver(key: K, observer: KeyedObserver<K>) =
synchronized(keyedObservers) {
- val observers = keyedObservers[key]
- if (observers?.remove(observer) != null && observers.isEmpty()) {
+ val observers = keyedObservers[key] ?: return false
+ val removed = observers.remove(observer) != null
+ if (removed && observers.isEmpty()) {
keyedObservers.remove(key)
}
+ removed
}
- }
override fun notifyChange(reason: Int) {
// make a copy to avoid potential ConcurrentModificationException
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
new file mode 100644
index 0000000..4aef0fc
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.datastore
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings.Global
+import android.provider.Settings.SettingNotFoundException
+
+/**
+ * [KeyValueStore] for [Global] settings.
+ *
+ * By default, a boolean type `true` value is stored as `1` and `false` value is stored as `0`.
+ */
+class SettingsGlobalStore private constructor(contentResolver: ContentResolver) :
+ SettingsStore(contentResolver) {
+
+ override val tag: String
+ get() = "SettingsGlobalStore"
+
+ override fun contains(key: String): Boolean = Global.getString(contentResolver, key) != null
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
+ try {
+ when (valueType) {
+ Boolean::class.javaObjectType -> Global.getInt(contentResolver, key) != 0
+ Float::class.javaObjectType -> Global.getFloat(contentResolver, key)
+ Int::class.javaObjectType -> Global.getInt(contentResolver, key)
+ Long::class.javaObjectType -> Global.getLong(contentResolver, key)
+ String::class.javaObjectType -> Global.getString(contentResolver, key)
+ else -> throw UnsupportedOperationException("Get $key $valueType")
+ }
+ as T?
+ } catch (e: SettingNotFoundException) {
+ null
+ }
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value == null) {
+ Global.putString(contentResolver, key, null)
+ return
+ }
+ when (valueType) {
+ Boolean::class.javaObjectType ->
+ Global.putInt(contentResolver, key, if (value == true) 1 else 0)
+ Float::class.javaObjectType -> Global.putFloat(contentResolver, key, value as Float)
+ Int::class.javaObjectType -> Global.putInt(contentResolver, key, value as Int)
+ Long::class.javaObjectType -> Global.putLong(contentResolver, key, value as Long)
+ String::class.javaObjectType -> Global.putString(contentResolver, key, value as String)
+ else -> throw UnsupportedOperationException("Set $key $valueType")
+ }
+ }
+
+ companion object {
+ @Volatile private var instance: SettingsGlobalStore? = null
+
+ @JvmStatic
+ fun get(context: Context): SettingsGlobalStore =
+ instance
+ ?: synchronized(this) {
+ instance
+ ?: SettingsGlobalStore(context.applicationContext.contentResolver).also {
+ instance = it
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
new file mode 100644
index 0000000..9f41ecbc
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.datastore
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings.Secure
+import android.provider.Settings.SettingNotFoundException
+
+/**
+ * [KeyValueStore] for [Secure] settings.
+ *
+ * By default, a boolean type `true` value is stored as `1` and `false` value is stored as `0`.
+ */
+class SettingsSecureStore private constructor(contentResolver: ContentResolver) :
+ SettingsStore(contentResolver) {
+
+ override val tag: String
+ get() = "SettingsSecureStore"
+
+ override fun contains(key: String): Boolean = Secure.getString(contentResolver, key) != null
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
+ try {
+ when (valueType) {
+ Boolean::class.javaObjectType -> Secure.getInt(contentResolver, key) != 0
+ Float::class.javaObjectType -> Secure.getFloat(contentResolver, key)
+ Int::class.javaObjectType -> Secure.getInt(contentResolver, key)
+ Long::class.javaObjectType -> Secure.getLong(contentResolver, key)
+ String::class.javaObjectType -> Secure.getString(contentResolver, key)
+ else -> throw UnsupportedOperationException("Get $key $valueType")
+ }
+ as T?
+ } catch (e: SettingNotFoundException) {
+ null
+ }
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value == null) {
+ Secure.putString(contentResolver, key, null)
+ return
+ }
+ when (valueType) {
+ Boolean::class.javaObjectType ->
+ Secure.putInt(contentResolver, key, if (value == true) 1 else 0)
+ Float::class.javaObjectType -> Secure.putFloat(contentResolver, key, value as Float)
+ Int::class.javaObjectType -> Secure.putInt(contentResolver, key, value as Int)
+ Long::class.javaObjectType -> Secure.putLong(contentResolver, key, value as Long)
+ String::class.javaObjectType -> Secure.putString(contentResolver, key, value as String)
+ else -> throw UnsupportedOperationException("Set $key $valueType")
+ }
+ }
+
+ companion object {
+ @Volatile private var instance: SettingsSecureStore? = null
+
+ @JvmStatic
+ fun get(context: Context): SettingsSecureStore =
+ instance
+ ?: synchronized(this) {
+ instance
+ ?: SettingsSecureStore(context.applicationContext.contentResolver).also {
+ instance = it
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
new file mode 100644
index 0000000..5981688
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.datastore
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicInteger
+
+/** Base class of the Settings provider data stores. */
+open abstract class SettingsStore(protected val contentResolver: ContentResolver) :
+ KeyedDataObservable<String>(), KeyValueStore {
+
+ /**
+ * Counter of observers.
+ *
+ * The value is accurate only when [addObserver] and [removeObserver] are called correctly. When
+ * an observer is not removed (and its weak reference is garbage collected), the content
+ * observer is not unregistered but this is not a big deal.
+ */
+ private val counter = AtomicInteger()
+
+ private val contentObserver =
+ object : ContentObserver(Handler(Looper.getMainLooper())) {
+ override fun onChange(selfChange: Boolean) {
+ super.onChange(selfChange, null)
+ }
+
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ val key = uri?.lastPathSegment ?: return
+ notifyChange(key, DataChangeReason.UPDATE)
+ }
+ }
+
+ override fun addObserver(observer: KeyedObserver<String?>, executor: Executor) =
+ if (super.addObserver(observer, executor)) {
+ onObserverAdded()
+ true
+ } else {
+ false
+ }
+
+ override fun addObserver(key: String, observer: KeyedObserver<String>, executor: Executor) =
+ if (super.addObserver(key, observer, executor)) {
+ onObserverAdded()
+ true
+ } else {
+ false
+ }
+
+ private fun onObserverAdded() {
+ if (counter.getAndIncrement() != 0) return
+ Log.i(tag, "registerContentObserver")
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(""),
+ true,
+ contentObserver,
+ )
+ }
+
+ override fun removeObserver(observer: KeyedObserver<String?>) =
+ if (super.removeObserver(observer)) {
+ onObserverRemoved()
+ true
+ } else {
+ false
+ }
+
+ override fun removeObserver(key: String, observer: KeyedObserver<String>) =
+ if (super.removeObserver(key, observer)) {
+ onObserverRemoved()
+ true
+ } else {
+ false
+ }
+
+ private fun onObserverRemoved() {
+ if (counter.decrementAndGet() != 0) return
+ Log.i(tag, "unregisterContentObserver")
+ contentResolver.unregisterContentObserver(contentObserver)
+ }
+
+ /** Tag for logging. */
+ abstract val tag: String
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
new file mode 100644
index 0000000..6cca7ed
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.datastore
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings.SettingNotFoundException
+import android.provider.Settings.System
+
+/**
+ * [KeyValueStore] for [System] settings.
+ *
+ * By default, a boolean type `true` value is stored as `1` and `false` value is stored as `0`.
+ */
+class SettingsSystemStore private constructor(contentResolver: ContentResolver) :
+ SettingsStore(contentResolver) {
+
+ override val tag: String
+ get() = "SettingsSystemStore"
+
+ override fun contains(key: String): Boolean = System.getString(contentResolver, key) != null
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
+ try {
+ when (valueType) {
+ Boolean::class.javaObjectType -> System.getInt(contentResolver, key) != 0
+ Float::class.javaObjectType -> System.getFloat(contentResolver, key)
+ Int::class.javaObjectType -> System.getInt(contentResolver, key)
+ Long::class.javaObjectType -> System.getLong(contentResolver, key)
+ String::class.javaObjectType -> System.getString(contentResolver, key)
+ else -> throw UnsupportedOperationException("Get $key $valueType")
+ }
+ as T?
+ } catch (e: SettingNotFoundException) {
+ null
+ }
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value == null) {
+ System.putString(contentResolver, key, null)
+ return
+ }
+ when (valueType) {
+ Boolean::class.javaObjectType ->
+ System.putInt(contentResolver, key, if (value == true) 1 else 0)
+ Float::class.javaObjectType -> System.putFloat(contentResolver, key, value as Float)
+ Int::class.javaObjectType -> System.putInt(contentResolver, key, value as Int)
+ Long::class.javaObjectType -> System.putLong(contentResolver, key, value as Long)
+ String::class.javaObjectType -> System.putString(contentResolver, key, value as String)
+ else -> throw UnsupportedOperationException("Set $key $valueType")
+ }
+ }
+
+ companion object {
+ @Volatile private var instance: SettingsSystemStore? = null
+
+ @JvmStatic
+ fun get(context: Context): SettingsSystemStore =
+ instance
+ ?: synchronized(this) {
+ instance
+ ?: SettingsSystemStore(context.applicationContext.contentResolver).also {
+ instance = it
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
index 0ca91cd..ea17a56 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
@@ -40,8 +40,9 @@
* Note that existing entries in the SharedPreferences will NOT be deleted before restore.
*
* @param context Context to get SharedPreferences
- * @param name Name of the SharedPreferences
- * @param mode Operating mode, see [Context.getSharedPreferences]
+ * @param name Name of the backup restore storage
+ * @param sharedPreferences SharedPreferences object
+ * @param filePath shared preferences file path relative to data dir
* @param verbose Verbose logging on key/value pairs during backup/restore. Enable for dev only!
* @param filter Filter of key/value pairs for backup and restore.
*/
@@ -50,12 +51,14 @@
constructor(
context: Context,
override val name: String,
- @get:VisibleForTesting internal val sharedPreferences: SharedPreferences,
+ override val sharedPreferences: SharedPreferences,
+ filePath: String = getSharedPreferencesFilePath(context, name),
private val codec: BackupCodec? = null,
private val verbose: Boolean = defaultVerbose(),
private val filter: (String, Any?) -> Boolean = { _, _ -> true },
) :
- BackupRestoreFileStorage(context, context.getSharedPreferencesFilePath(name)),
+ BackupRestoreFileStorage(context, filePath),
+ SharedPreferencesKeyValueStore,
KeyedObservable<String> by KeyedDataObservable() {
@JvmOverloads
@@ -66,7 +69,15 @@
codec: BackupCodec? = null,
verbose: Boolean = defaultVerbose(),
filter: (String, Any?) -> Boolean = { _, _ -> true },
- ) : this(context, name, context.getSharedPreferences(name, mode), codec, verbose, filter)
+ ) : this(
+ context,
+ name,
+ context.getSharedPreferences(name, mode),
+ getSharedPreferencesFilePath(context, name),
+ codec,
+ verbose,
+ filter,
+ )
/** Name of the intermediate SharedPreferences. */
@VisibleForTesting
@@ -80,7 +91,15 @@
return context.getSharedPreferences(intermediateName, Context.MODE_MULTI_PROCESS)
}
- private val sharedPreferencesListener = createSharedPreferenceListener()
+ private val sharedPreferencesListener =
+ SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
+ if (key != null) {
+ notifyChange(key, DataChangeReason.UPDATE)
+ } else {
+ // On Android >= R, SharedPreferences.Editor.clear() will trigger this case
+ notifyChange(DataChangeReason.DELETE)
+ }
+ }
init {
// listener is weakly referenced, so unregister is optional
@@ -183,7 +202,8 @@
else -> {
Log.e(
LOG_TAG,
- "[$name] $operation $key=$value, unknown type: ${value?.javaClass}")
+ "[$name] $operation $key=$value, unknown type: ${value?.javaClass}",
+ )
}
}
}
@@ -191,14 +211,31 @@
}
companion object {
- private fun Context.getSharedPreferencesFilePath(name: String): String {
- val file = getSharedPreferencesFile(name)
- return file.relativeTo(dataDirCompat).toString()
+ /** Returns the storage object of default [SharedPreferences]. */
+ @JvmStatic
+ fun getDefault(context: Context, name: String): SharedPreferencesStorage {
+ val prefName = getDefaultSharedPreferencesName(context)
+ return SharedPreferencesStorage(
+ context,
+ name,
+ context.getSharedPreferences(prefName, Context.MODE_PRIVATE),
+ getSharedPreferencesFilePath(context, prefName),
+ )
+ }
+
+ /** Returns the name of default [SharedPreferences]. */
+ @JvmStatic
+ fun getDefaultSharedPreferencesName(context: Context) = context.packageName + "_preferences"
+
+ /** Returns the shared preferences file path relative to data dir. */
+ @JvmStatic
+ fun getSharedPreferencesFilePath(context: Context, name: String): String {
+ val file = context.getSharedPreferencesFile(name)
+ return file.relativeTo(context.dataDirCompat).toString()
}
/** Returns the absolute path of shared preferences file. */
- @JvmStatic
- fun Context.getSharedPreferencesFile(name: String): File {
+ private fun Context.getSharedPreferencesFile(name: String): File {
// ContextImpl.getSharedPreferencesPath is private
return File(getSharedPreferencesDir(), "$name.xml")
}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
index 0fdecb0..c99d4b3 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
@@ -45,14 +45,14 @@
@Test
fun addObserver_sameExecutor() {
- keyedObservable.addObserver(observer1, executor1)
- keyedObservable.addObserver(observer1, executor1)
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isFalse()
}
@Test
fun addObserver_keyedObserver_sameExecutor() {
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isFalse()
}
@Test
@@ -109,15 +109,15 @@
@Test
fun addObserver_notifyObservers_removeObserver() {
- keyedObservable.addObserver(observer1, executor1)
- keyedObservable.addObserver(observer2, executor2)
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(observer2, executor2)).isTrue()
keyedObservable.notifyChange(DataChangeReason.UPDATE)
verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE)
verify(observer2).onKeyChanged(null, DataChangeReason.UPDATE)
reset(observer1, observer2)
- keyedObservable.removeObserver(observer2)
+ assertThat(keyedObservable.removeObserver(observer2)).isTrue()
keyedObservable.notifyChange(DataChangeReason.DELETE)
verify(observer1).onKeyChanged(null, DataChangeReason.DELETE)
@@ -126,15 +126,15 @@
@Test
fun addObserver_keyedObserver_notifyObservers_removeObserver() {
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
- keyedObservable.addObserver(key2, keyedObserver2, executor2)
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(key2, keyedObserver2, executor2)).isTrue()
keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE)
verify(keyedObserver2, never()).onKeyChanged(key2, DataChangeReason.UPDATE)
reset(keyedObserver1, keyedObserver2)
- keyedObservable.removeObserver(key1, keyedObserver1)
+ assertThat(keyedObservable.removeObserver(key1, keyedObserver1)).isTrue()
keyedObservable.notifyChange(key1, DataChangeReason.DELETE)
verify(keyedObserver1, never()).onKeyChanged(key1, DataChangeReason.DELETE)
@@ -143,9 +143,9 @@
@Test
fun notifyChange_addMoreTypeObservers_checkOnKeyChanged() {
- keyedObservable.addObserver(observer1, executor1)
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
- keyedObservable.addObserver(key2, keyedObserver2, executor1)
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(key2, keyedObserver2, executor1)).isTrue()
keyedObservable.notifyChange(DataChangeReason.UPDATE)
verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE)
@@ -171,25 +171,25 @@
fun notifyChange_addObserverWithinCallback() {
// ConcurrentModificationException is raised if it is not implemented correctly
val observer: KeyedObserver<Any?> = KeyedObserver { _, _ ->
- keyedObservable.addObserver(observer1, executor1)
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isTrue()
}
- keyedObservable.addObserver(observer, executor1)
+ assertThat(keyedObservable.addObserver(observer, executor1)).isTrue()
keyedObservable.notifyChange(DataChangeReason.UPDATE)
- keyedObservable.removeObserver(observer)
+ assertThat(keyedObservable.removeObserver(observer)).isTrue()
}
@Test
fun notifyChange_KeyedObserver_addObserverWithinCallback() {
// ConcurrentModificationException is raised if it is not implemented correctly
val keyObserver: KeyedObserver<Any?> = KeyedObserver { _, _ ->
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isTrue()
}
- keyedObservable.addObserver(key1, keyObserver, executor1)
+ assertThat(keyedObservable.addObserver(key1, keyObserver, executor1)).isTrue()
keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
- keyedObservable.removeObserver(key1, keyObserver)
+ assertThat(keyedObservable.removeObserver(key1, keyObserver)).isTrue()
}
}
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 4387b6f..a0599bb 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -35,6 +35,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
+import androidx.annotation.Nullable;
import androidx.annotation.RawRes;
import androidx.annotation.StringRes;
import androidx.preference.Preference;
@@ -243,6 +244,14 @@
}
/**
+ * Gets the content description set by {@link #setContentDescription}.
+ */
+ @Nullable
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
* Gets the lottie illustration resource id.
*/
public int getLottieAnimationResId() {
diff --git a/packages/SettingsLib/Metadata/Android.bp b/packages/SettingsLib/Metadata/Android.bp
new file mode 100644
index 0000000..207637f
--- /dev/null
+++ b/packages/SettingsLib/Metadata/Android.bp
@@ -0,0 +1,23 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "SettingsLibMetadata-srcs",
+ srcs: ["src/**/*.kt"],
+}
+
+android_library {
+ name: "SettingsLibMetadata",
+ defaults: [
+ "SettingsLintDefaults",
+ ],
+ srcs: [":SettingsLibMetadata-srcs"],
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.fragment_fragment",
+ "guava",
+ "SettingsLibDataStore",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SettingsLib/Metadata/AndroidManifest.xml b/packages/SettingsLib/Metadata/AndroidManifest.xml
new file mode 100644
index 0000000..1c801e6
--- /dev/null
+++ b/packages/SettingsLib/Metadata/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.metadata">
+
+ <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/packages/SettingsLib/Metadata/processor/Android.bp b/packages/SettingsLib/Metadata/processor/Android.bp
new file mode 100644
index 0000000..d8acc76
--- /dev/null
+++ b/packages/SettingsLib/Metadata/processor/Android.bp
@@ -0,0 +1,11 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_plugin {
+ name: "SettingsLibMetadata-processor",
+ srcs: ["src/**/*.kt"],
+ processor_class: "com.android.settingslib.metadata.PreferenceScreenAnnotationProcessor",
+ java_resource_dirs: ["resources"],
+ visibility: ["//visibility:public"],
+}
diff --git a/packages/SettingsLib/Metadata/processor/resources/META-INF/services/javax.annotation.processing.Processor b/packages/SettingsLib/Metadata/processor/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..762a01a
--- /dev/null
+++ b/packages/SettingsLib/Metadata/processor/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+com.android.settingslib.metadata.PreferenceScreenAnnotationProcessor
\ No newline at end of file
diff --git a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
new file mode 100644
index 0000000..620d717
--- /dev/null
+++ b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
@@ -0,0 +1,226 @@
+/*
+ * 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.metadata
+
+import java.util.TreeMap
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.ProcessingEnvironment
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.AnnotationMirror
+import javax.lang.model.element.AnnotationValue
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeMirror
+import javax.tools.Diagnostic
+
+/** Processor to gather preference screens annotated with `@ProvidePreferenceScreen`. */
+class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
+ private val screens = TreeMap<String, ConstructorType>()
+ private val overlays = mutableMapOf<String, String>()
+ private val contextType: TypeMirror by lazy {
+ processingEnv.elementUtils.getTypeElement("android.content.Context").asType()
+ }
+
+ private var options: Map<String, Any?>? = null
+ private lateinit var annotationElement: TypeElement
+ private lateinit var optionsElement: TypeElement
+ private lateinit var screenType: TypeMirror
+
+ override fun getSupportedAnnotationTypes() = setOf(ANNOTATION, OPTIONS)
+
+ override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported()
+
+ override fun init(processingEnv: ProcessingEnvironment) {
+ super.init(processingEnv)
+ val elementUtils = processingEnv.elementUtils
+ annotationElement = elementUtils.getTypeElement(ANNOTATION)
+ optionsElement = elementUtils.getTypeElement(OPTIONS)
+ screenType = elementUtils.getTypeElement("$PACKAGE.$PREFERENCE_SCREEN_METADATA").asType()
+ }
+
+ override fun process(
+ annotations: MutableSet<out TypeElement>,
+ roundEnv: RoundEnvironment,
+ ): Boolean {
+ roundEnv.getElementsAnnotatedWith(optionsElement).singleOrNull()?.run {
+ if (options != null) error("@$OPTIONS_NAME is already specified: $options", this)
+ options =
+ annotationMirrors
+ .single { it.isElement(optionsElement) }
+ .elementValues
+ .entries
+ .associate { it.key.simpleName.toString() to it.value.value }
+ }
+ for (element in roundEnv.getElementsAnnotatedWith(annotationElement)) {
+ (element as? TypeElement)?.process()
+ }
+ if (roundEnv.processingOver()) codegen()
+ return false
+ }
+
+ private fun TypeElement.process() {
+ if (kind != ElementKind.CLASS || modifiers.contains(Modifier.ABSTRACT)) {
+ error("@$ANNOTATION_NAME must be added to non abstract class", this)
+ return
+ }
+ if (!processingEnv.typeUtils.isAssignable(asType(), screenType)) {
+ error("@$ANNOTATION_NAME must be added to $PREFERENCE_SCREEN_METADATA subclass", this)
+ return
+ }
+ val constructorType = getConstructorType()
+ if (constructorType == null) {
+ error(
+ "Class must be an object, or has single public constructor that " +
+ "accepts no parameter or a Context parameter",
+ this,
+ )
+ return
+ }
+ val screenQualifiedName = qualifiedName.toString()
+ screens[screenQualifiedName] = constructorType
+ val annotation = annotationMirrors.single { it.isElement(annotationElement) }
+ val overlay = annotation.getOverlay()
+ if (overlay != null) {
+ overlays.put(overlay, screenQualifiedName)?.let {
+ error("$overlay has been overlaid by $it", this)
+ }
+ }
+ }
+
+ private fun codegen() {
+ val collector = (options?.get("codegenCollector") as? String) ?: DEFAULT_COLLECTOR
+ if (collector.isEmpty()) return
+ val parts = collector.split('/')
+ if (parts.size == 3) {
+ generateCode(parts[0], parts[1], parts[2])
+ } else {
+ throw IllegalArgumentException(
+ "Collector option '$collector' does not follow 'PKG/CLASS/METHOD' format"
+ )
+ }
+ }
+
+ private fun generateCode(outputPkg: String, outputClass: String, outputFun: String) {
+ for ((overlay, screen) in overlays) {
+ if (screens.remove(overlay) == null) {
+ warn("$overlay is overlaid by $screen but not annotated with @$ANNOTATION_NAME")
+ } else {
+ processingEnv.messager.printMessage(
+ Diagnostic.Kind.NOTE,
+ "$overlay is overlaid by $screen",
+ )
+ }
+ }
+ processingEnv.filer.createSourceFile("$outputPkg.$outputClass").openWriter().use {
+ it.write("package $outputPkg;\n\n")
+ it.write("import $PACKAGE.$PREFERENCE_SCREEN_METADATA;\n\n")
+ it.write("// Generated by annotation processor for @$ANNOTATION_NAME\n")
+ it.write("public final class $outputClass {\n")
+ it.write(" private $outputClass() {}\n\n")
+ it.write(
+ " public static java.util.List<$PREFERENCE_SCREEN_METADATA> " +
+ "$outputFun(android.content.Context context) {\n"
+ )
+ it.write(
+ " java.util.ArrayList<$PREFERENCE_SCREEN_METADATA> screens = " +
+ "new java.util.ArrayList<>(${screens.size});\n"
+ )
+ for ((screen, constructorType) in screens) {
+ when (constructorType) {
+ ConstructorType.DEFAULT -> it.write(" screens.add(new $screen());\n")
+ ConstructorType.CONTEXT -> it.write(" screens.add(new $screen(context));\n")
+ ConstructorType.SINGLETON -> it.write(" screens.add($screen.INSTANCE);\n")
+ }
+ }
+ for ((overlay, screen) in overlays) {
+ it.write(" // $overlay is overlaid by $screen\n")
+ }
+ it.write(" return screens;\n")
+ it.write(" }\n")
+ it.write("}")
+ }
+ }
+
+ private fun AnnotationMirror.isElement(element: TypeElement) =
+ processingEnv.typeUtils.isSameType(annotationType.asElement().asType(), element.asType())
+
+ private fun AnnotationMirror.getOverlay(): String? {
+ for ((key, value) in elementValues) {
+ if (key.simpleName.contentEquals("overlay")) {
+ return if (value.isDefaultClassValue(key)) null else value.value.toString()
+ }
+ }
+ return null
+ }
+
+ private fun AnnotationValue.isDefaultClassValue(key: ExecutableElement) =
+ processingEnv.typeUtils.isSameType(
+ value as TypeMirror,
+ key.defaultValue.value as TypeMirror,
+ )
+
+ private fun TypeElement.getConstructorType(): ConstructorType? {
+ var constructor: ExecutableElement? = null
+ for (element in enclosedElements) {
+ if (element.isKotlinObject()) return ConstructorType.SINGLETON
+ if (element.kind != ElementKind.CONSTRUCTOR) continue
+ if (!element.modifiers.contains(Modifier.PUBLIC)) continue
+ if (constructor != null) return null
+ constructor = element as ExecutableElement
+ }
+ return constructor?.parameters?.run {
+ when {
+ isEmpty() -> ConstructorType.DEFAULT
+ size == 1 && processingEnv.typeUtils.isSameType(this[0].asType(), contextType) ->
+ ConstructorType.CONTEXT
+ else -> null
+ }
+ }
+ }
+
+ private fun Element.isKotlinObject() =
+ kind == ElementKind.FIELD &&
+ modifiers.run { contains(Modifier.PUBLIC) && contains(Modifier.STATIC) } &&
+ simpleName.toString() == "INSTANCE"
+
+ private fun warn(msg: CharSequence) =
+ processingEnv.messager.printMessage(Diagnostic.Kind.WARNING, msg)
+
+ private fun error(msg: CharSequence, element: Element) =
+ processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg, element)
+
+ private enum class ConstructorType {
+ DEFAULT, // default constructor with no parameter
+ CONTEXT, // constructor with a Context parameter
+ SINGLETON, // Kotlin object class
+ }
+
+ companion object {
+ private const val PACKAGE = "com.android.settingslib.metadata"
+ private const val ANNOTATION_NAME = "ProvidePreferenceScreen"
+ private const val ANNOTATION = "$PACKAGE.$ANNOTATION_NAME"
+ private const val PREFERENCE_SCREEN_METADATA = "PreferenceScreenMetadata"
+
+ private const val OPTIONS_NAME = "ProvidePreferenceScreenOptions"
+ private const val OPTIONS = "$PACKAGE.$OPTIONS_NAME"
+ private const val DEFAULT_COLLECTOR = "$PACKAGE/PreferenceScreenCollector/get"
+ }
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
new file mode 100644
index 0000000..ea20a74
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.metadata
+
+import kotlin.reflect.KClass
+
+/**
+ * Annotation to provide preference screen.
+ *
+ * The annotated class must satisfy either condition:
+ * - the primary constructor has no parameter
+ * - the primary constructor has a single [android.content.Context] parameter
+ * - it is a Kotlin object class
+ *
+ * @param overlay if specified, current annotated screen will overlay the given screen
+ */
+@Retention(AnnotationRetention.SOURCE)
+@Target(AnnotationTarget.CLASS)
+@MustBeDocumented
+annotation class ProvidePreferenceScreen(
+ val overlay: KClass<out PreferenceScreenMetadata> = PreferenceScreenMetadata::class,
+)
+
+/**
+ * Provides options for [ProvidePreferenceScreen] annotation processor.
+ *
+ * @param codegenCollector generated collector class (format: "pkg/class/method"), an empty string
+ * means do not generate code
+ */
+@Retention(AnnotationRetention.SOURCE)
+@Target(AnnotationTarget.CLASS)
+@MustBeDocumented
+annotation class ProvidePreferenceScreenOptions(
+ val codegenCollector: String = "com.android.settingslib.metadata/PreferenceScreenCollector/get",
+)
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
new file mode 100644
index 0000000..51a8580
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
@@ -0,0 +1,174 @@
+/*
+ * 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.metadata
+
+import android.content.Context
+import androidx.annotation.ArrayRes
+import androidx.annotation.IntDef
+import com.android.settingslib.datastore.KeyValueStore
+
+/** Permit of read and write request. */
+@IntDef(
+ ReadWritePermit.ALLOW,
+ ReadWritePermit.DISALLOW,
+ ReadWritePermit.REQUIRE_APP_PERMISSION,
+ ReadWritePermit.REQUIRE_USER_AGREEMENT,
+)
+@Retention(AnnotationRetention.SOURCE)
+annotation class ReadWritePermit {
+ companion object {
+ /** Allow to read/write value. */
+ const val ALLOW = 0
+ /** Disallow to read/write value (e.g. uid not allowed). */
+ const val DISALLOW = 1
+ /** Require (runtime/special) app permission from user explicitly. */
+ const val REQUIRE_APP_PERMISSION = 2
+ /** Require explicit user agreement (e.g. terms of service). */
+ const val REQUIRE_USER_AGREEMENT = 3
+ }
+}
+
+/** Preference interface that has a value persisted in datastore. */
+interface PersistentPreference<T> {
+
+ /**
+ * Returns the key-value storage of the preference.
+ *
+ * The default implementation returns the storage provided by
+ * [PreferenceScreenRegistry.getKeyValueStore].
+ */
+ fun storage(context: Context): KeyValueStore =
+ PreferenceScreenRegistry.getKeyValueStore(context, this as PreferenceMetadata)!!
+
+ /**
+ * Returns if the external application (identified by [callingUid]) has permission to read
+ * preference value.
+ *
+ * The underlying implementation does NOT need to check common states like isEnabled,
+ * isRestricted or isAvailable.
+ */
+ @ReadWritePermit
+ fun getReadPermit(context: Context, myUid: Int, callingUid: Int): Int =
+ PreferenceScreenRegistry.getReadPermit(
+ context,
+ myUid,
+ callingUid,
+ this as PreferenceMetadata,
+ )
+
+ /**
+ * Returns if the external application (identified by [callingUid]) has permission to write
+ * preference with given [value].
+ *
+ * The underlying implementation does NOT need to check common states like isEnabled,
+ * isRestricted or isAvailable.
+ */
+ @ReadWritePermit
+ fun getWritePermit(context: Context, value: T?, myUid: Int, callingUid: Int): Int =
+ PreferenceScreenRegistry.getWritePermit(
+ context,
+ value,
+ myUid,
+ callingUid,
+ this as PreferenceMetadata,
+ )
+}
+
+/** Descriptor of values. */
+sealed interface ValueDescriptor {
+
+ /** Returns if given value (represented by index) is valid. */
+ fun isValidValue(context: Context, index: Int): Boolean
+}
+
+/**
+ * A boolean type value.
+ *
+ * A zero value means `False`, otherwise it is `True`.
+ */
+interface BooleanValue : ValueDescriptor {
+ override fun isValidValue(context: Context, index: Int) = true
+}
+
+/** Value falls into a given array. */
+interface DiscreteValue<T> : ValueDescriptor {
+ @get:ArrayRes val values: Int
+
+ @get:ArrayRes val valuesDescription: Int
+
+ fun getValue(context: Context, index: Int): T
+}
+
+/**
+ * Value falls into a text array, whose element is [CharSequence] type.
+ *
+ * [values] resource is `<string-array>`.
+ */
+interface DiscreteTextValue : DiscreteValue<CharSequence> {
+ override fun isValidValue(context: Context, index: Int): Boolean {
+ if (index < 0) return false
+ return index < context.resources.getTextArray(values).size
+ }
+
+ override fun getValue(context: Context, index: Int): CharSequence =
+ context.resources.getTextArray(values)[index]
+}
+
+/**
+ * Value falls into a string array, whose element is [String] type.
+ *
+ * [values] resource is `<string-array>`.
+ */
+interface DiscreteStringValue : DiscreteValue<String> {
+ override fun isValidValue(context: Context, index: Int): Boolean {
+ if (index < 0) return false
+ return index < context.resources.getStringArray(values).size
+ }
+
+ override fun getValue(context: Context, index: Int): String =
+ context.resources.getStringArray(values)[index]
+}
+
+/**
+ * Value falls into an integer array.
+ *
+ * [values] resource is `<integer-array>`.
+ */
+interface DiscreteIntValue : DiscreteValue<Int> {
+ override fun isValidValue(context: Context, index: Int): Boolean {
+ if (index < 0) return false
+ return index < context.resources.getIntArray(values).size
+ }
+
+ override fun getValue(context: Context, index: Int): Int =
+ context.resources.getIntArray(values)[index]
+}
+
+/** Value is between a range. */
+interface RangeValue : ValueDescriptor {
+ /** The lower bound (inclusive) of the range. */
+ val minValue: Int
+
+ /** The upper bound (inclusive) of the range. */
+ val maxValue: Int
+
+ /** The increment step within the range. 0 means unset, which implies step size is 1. */
+ val incrementStep: Int
+ get() = 0
+
+ override fun isValidValue(context: Context, index: Int) = index in minValue..maxValue
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
new file mode 100644
index 0000000..4503738
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.metadata
+
+/** A node in preference hierarchy that is associated with [PreferenceMetadata]. */
+open class PreferenceHierarchyNode internal constructor(val metadata: PreferenceMetadata)
+
+/**
+ * Preference hierarchy describes the structure of preferences recursively.
+ *
+ * A root hierarchy represents a preference screen. A sub-hierarchy represents a preference group.
+ */
+class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
+ PreferenceHierarchyNode(metadata) {
+
+ private val children = mutableListOf<PreferenceHierarchyNode>()
+
+ /** Adds a preference to the hierarchy. */
+ operator fun PreferenceMetadata.unaryPlus() {
+ children.add(PreferenceHierarchyNode(this))
+ }
+
+ /**
+ * Adds preference screen with given key (as a placeholder) to the hierarchy.
+ *
+ * This is mainly to support Android Settings overlays. OEMs might want to custom some of the
+ * screens. In resource-based hierarchy, it leverages the resource overlay. In terms of DSL or
+ * programmatic hierarchy, it will be a problem to specify concrete screen metadata objects.
+ * Instead, use preference screen key as a placeholder in the hierarchy and screen metadata will
+ * be looked up from [PreferenceScreenRegistry] lazily at runtime.
+ *
+ * @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
+ */
+ operator fun String.unaryPlus() {
+ children.add(PreferenceHierarchyNode(PreferenceScreenRegistry[this]!!))
+ }
+
+ /** Adds a preference to the hierarchy. */
+ fun add(metadata: PreferenceMetadata) {
+ children.add(PreferenceHierarchyNode(metadata))
+ }
+
+ /** Adds a preference group to the hierarchy. */
+ operator fun PreferenceGroup.unaryPlus() = PreferenceHierarchy(this).also { children.add(it) }
+
+ /** Adds a preference group and returns its preference hierarchy. */
+ fun addGroup(metadata: PreferenceGroup): PreferenceHierarchy =
+ PreferenceHierarchy(metadata).also { children.add(it) }
+
+ /**
+ * Adds preference screen with given key (as a placeholder) to the hierarchy.
+ *
+ * This is mainly to support Android Settings overlays. OEMs might want to custom some of the
+ * screens. In resource-based hierarchy, it leverages the resource overlay. In terms of DSL or
+ * programmatic hierarchy, it will be a problem to specify concrete screen metadata objects.
+ * Instead, use preference screen key as a placeholder in the hierarchy and screen metadata will
+ * be looked up from [PreferenceScreenRegistry] lazily at runtime.
+ *
+ * @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
+ */
+ fun addPreferenceScreen(screenKey: String) {
+ children.add(PreferenceHierarchy(PreferenceScreenRegistry[screenKey]!!))
+ }
+
+ /** Extensions to add more preferences to the hierarchy. */
+ operator fun plusAssign(init: PreferenceHierarchy.() -> Unit) = init(this)
+
+ /** Traversals preference hierarchy and applies given action. */
+ fun forEach(action: (PreferenceHierarchyNode) -> Unit) {
+ for (it in children) action(it)
+ }
+
+ /** Traversals preference hierarchy and applies given action. */
+ suspend fun forEachAsync(action: suspend (PreferenceHierarchyNode) -> Unit) {
+ for (it in children) action(it)
+ }
+
+ /** Finds the [PreferenceMetadata] object associated with given key. */
+ fun find(key: String): PreferenceMetadata? {
+ if (metadata.key == key) return metadata
+ for (child in children) {
+ if (child is PreferenceHierarchy) {
+ val result = child.find(key)
+ if (result != null) return result
+ } else {
+ if (child.metadata.key == key) return child.metadata
+ }
+ }
+ return null
+ }
+
+ /** Returns all the [PreferenceMetadata]s appear in the hierarchy. */
+ fun getAllPreferences(): List<PreferenceMetadata> =
+ mutableListOf<PreferenceMetadata>().also { getAllPreferences(it) }
+
+ private fun getAllPreferences(result: MutableList<PreferenceMetadata>) {
+ result.add(metadata)
+ for (child in children) {
+ if (child is PreferenceHierarchy) {
+ child.getAllPreferences(result)
+ } else {
+ result.add(child.metadata)
+ }
+ }
+ }
+}
+
+/**
+ * Builder function to create [PreferenceHierarchy] in
+ * [DSL](https://kotlinlang.org/docs/type-safe-builders.html) manner.
+ */
+fun preferenceHierarchy(metadata: PreferenceMetadata, init: PreferenceHierarchy.() -> Unit) =
+ PreferenceHierarchy(metadata).also(init)
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
new file mode 100644
index 0000000..f39f3a0
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
@@ -0,0 +1,204 @@
+/*
+ * 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.metadata
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import androidx.annotation.AnyThread
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+
+/**
+ * Interface provides preference metadata (title, summary, icon, etc.).
+ *
+ * Besides the existing APIs, subclass could integrate with following interface to provide more
+ * information:
+ * - [PreferenceTitleProvider]: provide dynamic title content
+ * - [PreferenceSummaryProvider]: provide dynamic summary content (e.g. based on preference value)
+ * - [PreferenceAvailabilityProvider]: provide preference availability (e.g. based on flag)
+ * - [PreferenceLifecycleProvider]: provide the lifecycle callbacks and notify state change
+ *
+ * Notes:
+ * - UI framework support:
+ * - This class does not involve any UI logic, it is the data layer.
+ * - Subclass could integrate with datastore and UI widget to provide UI layer. For instance,
+ * `PreferenceBinding` supports Jetpack Preference binding.
+ * - Datastore:
+ * - Subclass should implement the [PersistentPreference] to note that current preference is
+ * persistent in datastore.
+ * - It is always recommended to support back up preference value changed by user. Typically,
+ * the back up and restore happen within datastore, the [allowBackup] API is to mark if
+ * current preference value should be backed up (backup allowed by default).
+ * - Preference indexing for search:
+ * - Override [isIndexable] API to mark if preference is indexable (enabled by default).
+ * - If [isIndexable] returns true, preference title and summary will be indexed with cache.
+ * More indexing data could be provided through [keywords].
+ * - Settings search will cache the preference title/summary/keywords for indexing. The cache is
+ * invalidated when system locale changed, app upgraded, etc.
+ * - Dynamic content is not suitable to be cached for indexing. Subclass that implements
+ * [PreferenceTitleProvider] / [PreferenceSummaryProvider] will not have its title / summary
+ * indexed.
+ */
+@AnyThread
+interface PreferenceMetadata {
+
+ /** Preference key. */
+ val key: String
+
+ /**
+ * Preference title resource id.
+ *
+ * Implement [PreferenceTitleProvider] if title is generated dynamically.
+ */
+ val title: Int
+ @StringRes get() = 0
+
+ /**
+ * Preference summary resource id.
+ *
+ * Implement [PreferenceSummaryProvider] if summary is generated dynamically (e.g. summary is
+ * provided per preference value)
+ */
+ val summary: Int
+ @StringRes get() = 0
+
+ /** Icon of the preference. */
+ val icon: Int
+ @DrawableRes get() = 0
+
+ /** Additional keywords for indexing. */
+ val keywords: Int
+ @StringRes get() = 0
+
+ /**
+ * Return the extras Bundle object associated with this preference.
+ *
+ * It is used to provide more information for metadata.
+ */
+ fun extras(context: Context): Bundle? = null
+
+ /**
+ * Returns if preference is indexable, default value is `true`.
+ *
+ * Return `false` only when the preference is always unavailable on current device. If it is
+ * conditional available, override [PreferenceAvailabilityProvider].
+ */
+ fun isIndexable(context: Context): Boolean = true
+
+ /**
+ * Returns if preference is enabled.
+ *
+ * UI framework normally does not allow user to interact with the preference widget when it is
+ * disabled.
+ *
+ * [dependencyOfEnabledState] is provided to support dependency, the [shouldDisableDependents]
+ * value of dependent preference is used to decide enabled state.
+ */
+ fun isEnabled(context: Context): Boolean {
+ val dependency = dependencyOfEnabledState(context) ?: return true
+ return !dependency.shouldDisableDependents(context)
+ }
+
+ /** Returns the key of depended preference to decide the enabled state. */
+ fun dependencyOfEnabledState(context: Context): PreferenceMetadata? = null
+
+ /** Returns whether this preference's dependents should be disabled. */
+ fun shouldDisableDependents(context: Context): Boolean = !isEnabled(context)
+
+ /** Returns if the preference is persistent in datastore. */
+ fun isPersistent(context: Context): Boolean = this is PersistentPreference<*>
+
+ /**
+ * Returns if preference value backup is allowed (by default returns `true` if preference is
+ * persistent).
+ */
+ fun allowBackup(context: Context): Boolean = isPersistent(context)
+
+ /** Returns preference intent. */
+ fun intent(context: Context): Intent? = null
+
+ /** Returns preference order. */
+ fun order(context: Context): Int? = null
+
+ /**
+ * Returns the preference title.
+ *
+ * Implement [PreferenceTitleProvider] interface if title content is generated dynamically.
+ */
+ fun getPreferenceTitle(context: Context): CharSequence? =
+ when {
+ title != 0 -> context.getText(title)
+ this is PreferenceTitleProvider -> getTitle(context)
+ else -> null
+ }
+
+ /**
+ * Returns the preference summary.
+ *
+ * Implement [PreferenceSummaryProvider] interface if summary content is generated dynamically
+ * (e.g. summary is provided per preference value).
+ */
+ fun getPreferenceSummary(context: Context): CharSequence? =
+ when {
+ summary != 0 -> context.getText(summary)
+ this is PreferenceSummaryProvider -> getSummary(context)
+ else -> null
+ }
+}
+
+/** Metadata of preference group. */
+@AnyThread
+open class PreferenceGroup(override val key: String, override val title: Int) : PreferenceMetadata
+
+/** Metadata of preference screen. */
+@AnyThread
+interface PreferenceScreenMetadata : PreferenceMetadata {
+
+ /**
+ * The screen title resource, which precedes [getScreenTitle] if provided.
+ *
+ * By default, screen title is same with [title].
+ */
+ val screenTitle: Int
+ get() = title
+
+ /** Returns dynamic screen title, use [screenTitle] whenever possible. */
+ fun getScreenTitle(context: Context): CharSequence? = null
+
+ /** Returns the fragment class to show the preference screen. */
+ fun fragmentClass(): Class<out Fragment>?
+
+ /**
+ * Indicates if [getPreferenceHierarchy] returns a complete hierarchy of the preference screen.
+ *
+ * If `true`, the result of [getPreferenceHierarchy] will be used to inflate preference screen.
+ * Otherwise, it is an intermediate state called hybrid mode, preference hierarchy is
+ * represented by other ways (e.g. XML resource) and [PreferenceMetadata]s in
+ * [getPreferenceHierarchy] will only be used to bind UI widgets.
+ */
+ fun hasCompleteHierarchy(): Boolean = true
+
+ /**
+ * Returns the hierarchy of preference screen.
+ *
+ * The implementation MUST include all preferences into the hierarchy regardless of the runtime
+ * conditions. DO NOT check any condition (except compile time flag) before adding a preference.
+ */
+ fun getPreferenceHierarchy(context: Context): PreferenceHierarchy
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
new file mode 100644
index 0000000..84014f1
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.metadata
+
+import android.content.Context
+
+/** Provides the associated preference screen key for binding. */
+interface PreferenceScreenBindingKeyProvider {
+
+ /** Returns the associated preference screen key. */
+ fun getPreferenceScreenBindingKey(context: Context): String?
+}
+
+/** Extra key to provide the preference screen key for binding. */
+const val EXTRA_BINDING_SCREEN_KEY = "settingslib:binding_screen_key"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
new file mode 100644
index 0000000..48798da
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.metadata
+
+import android.content.Context
+import com.android.settingslib.datastore.KeyValueStore
+import com.google.common.base.Supplier
+import com.google.common.base.Suppliers
+import com.google.common.collect.ImmutableMap
+
+private typealias PreferenceScreenMap = ImmutableMap<String, PreferenceScreenMetadata>
+
+/** Registry of all available preference screens in the app. */
+object PreferenceScreenRegistry : ReadWritePermitProvider {
+
+ /** Provider of key-value store. */
+ private lateinit var keyValueStoreProvider: KeyValueStoreProvider
+
+ private var preferenceScreensSupplier: Supplier<PreferenceScreenMap> = Supplier {
+ ImmutableMap.of()
+ }
+
+ private val preferenceScreens: PreferenceScreenMap
+ get() = preferenceScreensSupplier.get()
+
+ private var readWritePermitProvider: ReadWritePermitProvider? = null
+
+ /** Sets the [KeyValueStoreProvider]. */
+ fun setKeyValueStoreProvider(keyValueStoreProvider: KeyValueStoreProvider) {
+ this.keyValueStoreProvider = keyValueStoreProvider
+ }
+
+ /**
+ * Returns the key-value store for given preference.
+ *
+ * Must call [setKeyValueStoreProvider] before invoking this method, otherwise
+ * [NullPointerException] is raised.
+ */
+ fun getKeyValueStore(context: Context, preference: PreferenceMetadata): KeyValueStore? =
+ keyValueStoreProvider.getKeyValueStore(context, preference)
+
+ /** Sets supplier to provide available preference screens. */
+ fun setPreferenceScreensSupplier(supplier: Supplier<List<PreferenceScreenMetadata>>) {
+ preferenceScreensSupplier =
+ Suppliers.memoize {
+ val screensBuilder = ImmutableMap.builder<String, PreferenceScreenMetadata>()
+ for (screen in supplier.get()) screensBuilder.put(screen.key, screen)
+ screensBuilder.buildOrThrow()
+ }
+ }
+
+ /** Sets available preference screens. */
+ fun setPreferenceScreens(vararg screens: PreferenceScreenMetadata) {
+ val screensBuilder = ImmutableMap.builder<String, PreferenceScreenMetadata>()
+ for (screen in screens) screensBuilder.put(screen.key, screen)
+ preferenceScreensSupplier = Suppliers.ofInstance(screensBuilder.buildOrThrow())
+ }
+
+ /** Returns [PreferenceScreenMetadata] of particular key. */
+ operator fun get(key: String?): PreferenceScreenMetadata? =
+ if (key != null) preferenceScreens[key] else null
+
+ /**
+ * Sets the provider to check read write permit. Read and write requests are denied by default.
+ */
+ fun setReadWritePermitProvider(readWritePermitProvider: ReadWritePermitProvider?) {
+ this.readWritePermitProvider = readWritePermitProvider
+ }
+
+ override fun getReadPermit(
+ context: Context,
+ myUid: Int,
+ callingUid: Int,
+ preference: PreferenceMetadata,
+ ) =
+ readWritePermitProvider?.getReadPermit(context, myUid, callingUid, preference)
+ ?: ReadWritePermit.DISALLOW
+
+ override fun getWritePermit(
+ context: Context,
+ value: Any?,
+ myUid: Int,
+ callingUid: Int,
+ preference: PreferenceMetadata,
+ ) =
+ readWritePermitProvider?.getWritePermit(context, value, myUid, callingUid, preference)
+ ?: ReadWritePermit.DISALLOW
+}
+
+/** Provider of [KeyValueStore]. */
+fun interface KeyValueStoreProvider {
+
+ /**
+ * Returns the key-value store for given preference.
+ *
+ * Here are some use cases:
+ * - provide the default storage for all preferences
+ * - determine the storage per preference keys or the interfaces implemented by the preference
+ */
+ fun getKeyValueStore(context: Context, preference: PreferenceMetadata): KeyValueStore?
+}
+
+/** Provider of read and write permit. */
+interface ReadWritePermitProvider {
+
+ @ReadWritePermit
+ fun getReadPermit(
+ context: Context,
+ myUid: Int,
+ callingUid: Int,
+ preference: PreferenceMetadata,
+ ): Int
+
+ @ReadWritePermit
+ fun getWritePermit(
+ context: Context,
+ value: Any?,
+ myUid: Int,
+ callingUid: Int,
+ preference: PreferenceMetadata,
+ ): Int
+
+ companion object {
+ @JvmField
+ val ALLOW_ALL_READ_WRITE =
+ object : ReadWritePermitProvider {
+ override fun getReadPermit(
+ context: Context,
+ myUid: Int,
+ callingUid: Int,
+ preference: PreferenceMetadata,
+ ) = ReadWritePermit.ALLOW
+
+ override fun getWritePermit(
+ context: Context,
+ value: Any?,
+ myUid: Int,
+ callingUid: Int,
+ preference: PreferenceMetadata,
+ ) = ReadWritePermit.ALLOW
+ }
+ }
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
new file mode 100644
index 0000000..a3aa85d
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.metadata
+
+import android.content.Context
+
+/**
+ * Interface to provide dynamic preference title.
+ *
+ * Implement this interface implies that the preference title should not be cached for indexing.
+ */
+interface PreferenceTitleProvider {
+
+ /** Provides preference title. */
+ fun getTitle(context: Context): CharSequence?
+}
+
+/**
+ * Interface to provide dynamic preference summary.
+ *
+ * Implement this interface implies that the preference summary should not be cached for indexing.
+ */
+interface PreferenceSummaryProvider {
+
+ /** Provides preference summary. */
+ fun getSummary(context: Context): CharSequence?
+}
+
+/**
+ * Interface to provide the state of preference availability.
+ *
+ * UI framework normally does not show the preference widget if it is unavailable.
+ */
+interface PreferenceAvailabilityProvider {
+
+ /** Returns if the preference is available. */
+ fun isAvailable(context: Context): Boolean
+}
+
+/**
+ * Interface to provide the managed configuration state of the preference.
+ *
+ * See [Managed configurations](https://developer.android.com/work/managed-configurations) for the
+ * Android Enterprise support.
+ */
+interface PreferenceRestrictionProvider {
+
+ /** Returns if preference is restricted by managed configs. */
+ fun isRestricted(context: Context): Boolean
+}
+
+/**
+ * Preference lifecycle to deal with preference state.
+ *
+ * Implement this interface when preference depends on runtime conditions.
+ */
+interface PreferenceLifecycleProvider {
+
+ /**
+ * Called when preference is attached to UI.
+ *
+ * Subclass could override this API to register runtime condition listeners, and invoke
+ * `onPreferenceStateChanged(this)` on the given [preferenceStateObserver] to update UI when
+ * internal state (e.g. availability, enabled state, title, summary) is changed.
+ */
+ fun onAttach(context: Context, preferenceStateObserver: PreferenceStateObserver)
+
+ /**
+ * Called when preference is detached from UI.
+ *
+ * Clean up and release resource.
+ */
+ fun onDetach(context: Context)
+
+ /** Observer of preference state. */
+ interface PreferenceStateObserver {
+
+ /** Callbacks when preference state is changed. */
+ fun onPreferenceStateChanged(preference: PreferenceMetadata)
+ }
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
new file mode 100644
index 0000000..ad996c7
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.metadata
+
+import android.content.Context
+import androidx.annotation.StringRes
+
+/**
+ * Common base class for preferences that have two selectable states, save a boolean value, and may
+ * have dependent preferences that are enabled/disabled based on the current state.
+ */
+interface TwoStatePreference : PreferenceMetadata, PersistentPreference<Boolean>, BooleanValue {
+
+ override fun shouldDisableDependents(context: Context) =
+ storage(context).getValue(key, Boolean::class.javaObjectType) != true ||
+ super.shouldDisableDependents(context)
+}
+
+/** A preference that provides a two-state toggleable option. */
+open class SwitchPreference
+@JvmOverloads
+constructor(
+ override val key: String,
+ @StringRes override val title: Int = 0,
+ @StringRes override val summary: Int = 0,
+) : TwoStatePreference
diff --git a/packages/SettingsLib/Preference/Android.bp b/packages/SettingsLib/Preference/Android.bp
new file mode 100644
index 0000000..17852e8
--- /dev/null
+++ b/packages/SettingsLib/Preference/Android.bp
@@ -0,0 +1,24 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "SettingsLibPreference-srcs",
+ srcs: ["src/**/*.kt"],
+}
+
+android_library {
+ name: "SettingsLibPreference",
+ defaults: [
+ "SettingsLintDefaults",
+ ],
+ srcs: [":SettingsLibPreference-srcs"],
+ static_libs: [
+ "SettingsLibDataStore",
+ "SettingsLibMetadata",
+ "androidx.annotation_annotation",
+ "androidx.preference_preference",
+ "guava",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SettingsLib/Preference/AndroidManifest.xml b/packages/SettingsLib/Preference/AndroidManifest.xml
new file mode 100644
index 0000000..2d7f7ba
--- /dev/null
+++ b/packages/SettingsLib/Preference/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.preference">
+
+ <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
new file mode 100644
index 0000000..9be0e71
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.preference
+
+import android.content.Context
+import androidx.preference.DialogPreference
+import androidx.preference.ListPreference
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import androidx.preference.SeekBarPreference
+import com.android.settingslib.metadata.DiscreteIntValue
+import com.android.settingslib.metadata.DiscreteValue
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceScreenMetadata
+import com.android.settingslib.metadata.RangeValue
+
+/** Binding of preference widget and preference metadata. */
+interface PreferenceBinding {
+
+ /**
+ * Provides a new [Preference] widget instance.
+ *
+ * By default, it returns a new [Preference] object. Subclass could override this method to
+ * provide customized widget and do **one-off** initialization (e.g.
+ * [Preference.setOnPreferenceClickListener]). To update widget everytime when state is changed,
+ * override the [bind] method.
+ *
+ * Notes:
+ * - DO NOT set any properties defined in [PreferenceMetadata]. For example,
+ * title/summary/icon/extras/isEnabled/isVisible/isPersistent/dependency. These properties
+ * will be reset by [bind].
+ * - Override [bind] if needed to provide more information for customized widget.
+ */
+ fun createWidget(context: Context): Preference = Preference(context)
+
+ /**
+ * Binds preference widget with given metadata.
+ *
+ * Whenever metadata state is changed, this callback is invoked to update widget. By default,
+ * the common states like title, summary, enabled, etc. are already applied. Subclass should
+ * override this method to bind more data (e.g. read preference value from storage and apply it
+ * to widget).
+ *
+ * @param preference preference widget created by [createWidget]
+ * @param metadata metadata to apply
+ */
+ fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ metadata.apply {
+ preference.key = key
+ if (icon != 0) {
+ preference.setIcon(icon)
+ } else {
+ preference.icon = null
+ }
+ val context = preference.context
+ preference.peekExtras()?.clear()
+ extras(context)?.let { preference.extras.putAll(it) }
+ preference.title = getPreferenceTitle(context)
+ preference.summary = getPreferenceSummary(context)
+ preference.isEnabled = isEnabled(context)
+ preference.isVisible =
+ (this as? PreferenceAvailabilityProvider)?.isAvailable(context) != false
+ preference.isPersistent = isPersistent(context)
+ metadata.order(context)?.let { preference.order = it }
+ // PreferenceRegistry will notify dependency change, so we do not need to set
+ // dependency here. This simplifies dependency management and avoid the
+ // IllegalStateException when call Preference.setDependency
+ preference.dependency = null
+ if (preference !is PreferenceScreen) { // avoid recursive loop when build graph
+ preference.fragment = (this as? PreferenceScreenCreator)?.fragmentClass()?.name
+ preference.intent = intent(context)
+ }
+ if (preference is DialogPreference) {
+ preference.dialogTitle = preference.title
+ }
+ if (preference is ListPreference && this is DiscreteValue<*>) {
+ preference.setEntries(valuesDescription)
+ if (this is DiscreteIntValue) {
+ val intValues = context.resources.getIntArray(values)
+ preference.entryValues = Array(intValues.size) { intValues[it].toString() }
+ } else {
+ preference.setEntryValues(values)
+ }
+ } else if (preference is SeekBarPreference && this is RangeValue) {
+ preference.min = minValue
+ preference.max = maxValue
+ preference.seekBarIncrement = incrementStep
+ }
+ }
+ }
+}
+
+/** Abstract preference screen to provide preference hierarchy and binding factory. */
+interface PreferenceScreenCreator : PreferenceScreenMetadata, PreferenceScreenProvider {
+
+ val preferenceBindingFactory: PreferenceBindingFactory
+ get() = DefaultPreferenceBindingFactory
+
+ override fun createPreferenceScreen(factory: PreferenceScreenFactory) =
+ factory.getOrCreatePreferenceScreen().apply {
+ inflatePreferenceHierarchy(preferenceBindingFactory, getPreferenceHierarchy(context))
+ }
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
new file mode 100644
index 0000000..4c2e1ba
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.preference
+
+import com.android.settingslib.metadata.PreferenceGroup
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.SwitchPreference
+
+/** Factory to map [PreferenceMetadata] to [PreferenceBinding]. */
+interface PreferenceBindingFactory {
+
+ /** Returns the [PreferenceBinding] associated with the [PreferenceMetadata]. */
+ fun getPreferenceBinding(metadata: PreferenceMetadata): PreferenceBinding?
+}
+
+/** Default [PreferenceBindingFactory]. */
+object DefaultPreferenceBindingFactory : PreferenceBindingFactory {
+
+ override fun getPreferenceBinding(metadata: PreferenceMetadata) =
+ metadata as? PreferenceBinding
+ ?: when (metadata) {
+ is SwitchPreference -> SwitchPreferenceBinding.INSTANCE
+ is PreferenceGroup -> PreferenceGroupBinding.INSTANCE
+ is PreferenceScreenCreator -> PreferenceScreenBinding.INSTANCE
+ else -> DefaultPreferenceBinding
+ }
+}
+
+/** A preference key based binding factory. */
+class KeyedPreferenceBindingFactory(private val bindings: Map<String, PreferenceBinding>) :
+ PreferenceBindingFactory {
+
+ override fun getPreferenceBinding(metadata: PreferenceMetadata) =
+ bindings[metadata.key] ?: DefaultPreferenceBindingFactory.getPreferenceBinding(metadata)
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
new file mode 100644
index 0000000..ede970e
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.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.settingslib.preference
+
+import android.content.Context
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceScreen
+import androidx.preference.SwitchPreferenceCompat
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
+import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceScreenMetadata
+import com.android.settingslib.metadata.PreferenceTitleProvider
+
+/** Binding of preference group associated with [PreferenceCategory]. */
+interface PreferenceScreenBinding : PreferenceBinding {
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ val context = preference.context
+ val screenMetadata = metadata as PreferenceScreenMetadata
+ // Pass the preference key to fragment, so that the fragment could find associated
+ // preference screen registered in PreferenceScreenRegistry
+ preference.extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+ if (preference is PreferenceScreen) {
+ val screenTitle = screenMetadata.screenTitle
+ preference.title =
+ if (screenTitle != 0) {
+ context.getString(screenTitle)
+ } else {
+ screenMetadata.getScreenTitle(context)
+ ?: (this as? PreferenceTitleProvider)?.getTitle(context)
+ }
+ }
+ }
+
+ companion object {
+ @JvmStatic val INSTANCE = object : PreferenceScreenBinding {}
+ }
+}
+
+/** Binding of preference group associated with [PreferenceCategory]. */
+interface PreferenceGroupBinding : PreferenceBinding {
+
+ override fun createWidget(context: Context) = PreferenceCategory(context)
+
+ companion object {
+ @JvmStatic val INSTANCE = object : PreferenceGroupBinding {}
+ }
+}
+
+/** A boolean value type preference associated with [SwitchPreferenceCompat]. */
+interface SwitchPreferenceBinding : PreferenceBinding {
+
+ override fun createWidget(context: Context): Preference = SwitchPreferenceCompat(context)
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ (metadata as? PersistentPreference<*>)
+ ?.storage(preference.context)
+ ?.getValue(metadata.key, Boolean::class.javaObjectType)
+ ?.let { (preference as SwitchPreferenceCompat).isChecked = it }
+ }
+
+ companion object {
+ @JvmStatic val INSTANCE = object : SwitchPreferenceBinding {}
+ }
+}
+
+/** Default [PreferenceBinding] for [Preference]. */
+object DefaultPreferenceBinding : PreferenceBinding
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
new file mode 100644
index 0000000..02acfca
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.preference
+
+import androidx.preference.PreferenceDataStore
+import com.android.settingslib.datastore.KeyValueStore
+
+/** Adapter to translate [KeyValueStore] into [PreferenceDataStore]. */
+class PreferenceDataStoreAdapter(private val keyValueStore: KeyValueStore) : PreferenceDataStore() {
+
+ override fun getBoolean(key: String, defValue: Boolean): Boolean =
+ keyValueStore.getValue(key, Boolean::class.javaObjectType) ?: defValue
+
+ override fun getFloat(key: String, defValue: Float): Float =
+ keyValueStore.getValue(key, Float::class.javaObjectType) ?: defValue
+
+ override fun getInt(key: String, defValue: Int): Int =
+ keyValueStore.getValue(key, Int::class.javaObjectType) ?: defValue
+
+ override fun getLong(key: String, defValue: Long): Long =
+ keyValueStore.getValue(key, Long::class.javaObjectType) ?: defValue
+
+ override fun getString(key: String, defValue: String?): String? =
+ keyValueStore.getValue(key, String::class.javaObjectType) ?: defValue
+
+ override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? =
+ (keyValueStore.getValue(key, Set::class.javaObjectType) as Set<String>?) ?: defValues
+
+ override fun putBoolean(key: String, value: Boolean) =
+ keyValueStore.setValue(key, Boolean::class.javaObjectType, value)
+
+ override fun putFloat(key: String, value: Float) =
+ keyValueStore.setValue(key, Float::class.javaObjectType, value)
+
+ override fun putInt(key: String, value: Int) =
+ keyValueStore.setValue(key, Int::class.javaObjectType, value)
+
+ override fun putLong(key: String, value: Long) =
+ keyValueStore.setValue(key, Long::class.javaObjectType, value)
+
+ override fun putString(key: String, value: String?) =
+ keyValueStore.setValue(key, String::class.javaObjectType, value)
+
+ override fun putStringSet(key: String, values: Set<String>?) =
+ keyValueStore.setValue(key, Set::class.javaObjectType, values)
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
new file mode 100644
index 0000000..68f640b
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.preference
+
+import android.content.Context
+import android.os.Bundle
+import androidx.annotation.XmlRes
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.PreferenceScreen
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
+import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
+import com.android.settingslib.metadata.PreferenceScreenRegistry
+import com.android.settingslib.preference.PreferenceScreenBindingHelper.Companion.bindRecursively
+
+/** Fragment to display a preference screen. */
+open class PreferenceFragment :
+ PreferenceFragmentCompat(), PreferenceScreenProvider, PreferenceScreenBindingKeyProvider {
+
+ private var preferenceScreenBindingHelper: PreferenceScreenBindingHelper? = null
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ preferenceScreen = createPreferenceScreen()
+ }
+
+ fun createPreferenceScreen(): PreferenceScreen? =
+ createPreferenceScreen(PreferenceScreenFactory(this))
+
+ override fun createPreferenceScreen(factory: PreferenceScreenFactory): PreferenceScreen? {
+ val context = factory.context
+ fun createPreferenceScreenFromResource() =
+ factory.inflate(getPreferenceScreenResId(context))
+
+ if (!usePreferenceScreenMetadata()) return createPreferenceScreenFromResource()
+
+ val screenKey = getPreferenceScreenBindingKey(context)
+ val screenCreator =
+ (PreferenceScreenRegistry[screenKey] as? PreferenceScreenCreator)
+ ?: return createPreferenceScreenFromResource()
+
+ val preferenceBindingFactory = screenCreator.preferenceBindingFactory
+ val preferenceHierarchy = screenCreator.getPreferenceHierarchy(context)
+ val preferenceScreen =
+ if (screenCreator.hasCompleteHierarchy()) {
+ factory.getOrCreatePreferenceScreen().apply {
+ inflatePreferenceHierarchy(preferenceBindingFactory, preferenceHierarchy)
+ }
+ } else {
+ createPreferenceScreenFromResource()?.also {
+ bindRecursively(it, preferenceBindingFactory, preferenceHierarchy)
+ } ?: return null
+ }
+ preferenceScreenBindingHelper =
+ PreferenceScreenBindingHelper(
+ context,
+ preferenceBindingFactory,
+ preferenceScreen,
+ preferenceHierarchy,
+ )
+ return preferenceScreen
+ }
+
+ /**
+ * Returns if preference screen metadata can be used to set up preference screen.
+ *
+ * This is for flagging purpose. If false (e.g. flag is disabled), xml resource is used to build
+ * preference screen.
+ */
+ protected open fun usePreferenceScreenMetadata(): Boolean = false
+
+ /** Returns the xml resource to create preference screen. */
+ @XmlRes protected open fun getPreferenceScreenResId(context: Context): Int = 0
+
+ override fun getPreferenceScreenBindingKey(context: Context): String? =
+ arguments?.getString(EXTRA_BINDING_SCREEN_KEY)
+
+ override fun onDestroy() {
+ preferenceScreenBindingHelper?.close()
+ super.onDestroy()
+ }
+
+ companion object {
+ /** Returns [PreferenceFragment] instance to display the preference screen of given key. */
+ fun of(screenKey: String): PreferenceFragment? {
+ val screenMetadata = PreferenceScreenRegistry[screenKey] ?: return null
+ if (
+ screenMetadata is PreferenceScreenCreator && screenMetadata.hasCompleteHierarchy()
+ ) {
+ return PreferenceFragment().apply {
+ arguments = Bundle().apply { putString(EXTRA_BINDING_SCREEN_KEY, screenKey) }
+ }
+ }
+ return null
+ }
+ }
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
new file mode 100644
index 0000000..5ef7823
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.preference
+
+import androidx.preference.PreferenceDataStore
+import androidx.preference.PreferenceGroup
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceHierarchy
+import com.android.settingslib.metadata.PreferenceMetadata
+
+/** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */
+fun PreferenceGroup.inflatePreferenceHierarchy(
+ preferenceBindingFactory: PreferenceBindingFactory,
+ hierarchy: PreferenceHierarchy,
+ storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(),
+) {
+ fun PreferenceMetadata.preferenceBinding() = preferenceBindingFactory.getPreferenceBinding(this)
+
+ hierarchy.metadata.let { it.preferenceBinding()?.bind(this, it) }
+ hierarchy.forEach {
+ val metadata = it.metadata
+ val preferenceBinding = metadata.preferenceBinding() ?: return@forEach
+ val widget = preferenceBinding.createWidget(context)
+ if (it is PreferenceHierarchy) {
+ val preferenceGroup = widget as PreferenceGroup
+ // MUST add preference before binding, otherwise exception is raised when add child
+ addPreference(preferenceGroup)
+ preferenceGroup.inflatePreferenceHierarchy(preferenceBindingFactory, it)
+ } else {
+ preferenceBinding.bind(widget, metadata)
+ (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
+ widget.preferenceDataStore =
+ storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
+ }
+ // MUST add preference after binding for persistent preference to get initial value
+ // (preference key is set within bind method)
+ addPreference(widget)
+ }
+ }
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
new file mode 100644
index 0000000..3610894
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -0,0 +1,200 @@
+/*
+ * 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.preference
+
+import android.content.Context
+import android.os.Handler
+import android.os.Looper
+import androidx.preference.Preference
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceScreen
+import com.android.settingslib.datastore.KeyedDataObservable
+import com.android.settingslib.datastore.KeyedObservable
+import com.android.settingslib.datastore.KeyedObserver
+import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceHierarchy
+import com.android.settingslib.metadata.PreferenceLifecycleProvider
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceScreenRegistry
+import com.google.common.collect.ImmutableMap
+import com.google.common.collect.ImmutableMultimap
+import java.util.concurrent.Executor
+
+/**
+ * Helper to bind preferences on given [preferenceScreen].
+ *
+ * When there is any preference change event detected (e.g. preference value changed, runtime
+ * states, dependency is updated), this helper class will re-bind [PreferenceMetadata] to update
+ * widget UI.
+ */
+class PreferenceScreenBindingHelper(
+ context: Context,
+ private val preferenceBindingFactory: PreferenceBindingFactory,
+ private val preferenceScreen: PreferenceScreen,
+ preferenceHierarchy: PreferenceHierarchy,
+) : KeyedDataObservable<String>(), AutoCloseable {
+
+ private val handler = Handler(Looper.getMainLooper())
+ private val executor =
+ object : Executor {
+ override fun execute(command: Runnable) {
+ handler.post(command)
+ }
+ }
+
+ private val preferences: ImmutableMap<String, PreferenceMetadata>
+ private val dependencies: ImmutableMultimap<String, String>
+ private val storages = mutableSetOf<KeyedObservable<String>>()
+
+ private val preferenceObserver: KeyedObserver<String?>
+
+ private val storageObserver =
+ KeyedObserver<String?> { key, _ ->
+ if (key != null) {
+ notifyChange(key, CHANGE_REASON_VALUE)
+ }
+ }
+
+ private val stateObserver =
+ object : PreferenceLifecycleProvider.PreferenceStateObserver {
+ override fun onPreferenceStateChanged(preference: PreferenceMetadata) {
+ notifyChange(preference.key, CHANGE_REASON_STATE)
+ }
+ }
+
+ init {
+ val preferencesBuilder = ImmutableMap.builder<String, PreferenceMetadata>()
+ val dependenciesBuilder = ImmutableMultimap.builder<String, String>()
+ fun PreferenceMetadata.addDependency(dependency: PreferenceMetadata) {
+ dependenciesBuilder.put(key, dependency.key)
+ }
+
+ fun PreferenceMetadata.add() {
+ preferencesBuilder.put(key, this)
+ dependencyOfEnabledState(context)?.addDependency(this)
+ if (this is PreferenceLifecycleProvider) onAttach(context, stateObserver)
+ if (this is PersistentPreference<*>) storages.add(storage(context))
+ }
+
+ fun PreferenceHierarchy.addPreferences() {
+ metadata.add()
+ forEach {
+ if (it is PreferenceHierarchy) {
+ it.addPreferences()
+ } else {
+ it.metadata.add()
+ }
+ }
+ }
+
+ preferenceHierarchy.addPreferences()
+ this.preferences = preferencesBuilder.buildOrThrow()
+ this.dependencies = dependenciesBuilder.build()
+
+ preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) }
+ addObserver(preferenceObserver, executor)
+ for (storage in storages) storage.addObserver(storageObserver, executor)
+ }
+
+ private fun onPreferenceChange(key: String?, reason: Int) {
+ if (key == null) return
+
+ // bind preference to update UI
+ preferenceScreen.findPreference<Preference>(key)?.let {
+ preferenceBindingFactory.bind(it, preferences[key])
+ }
+
+ // check reason to avoid potential infinite loop
+ if (reason != CHANGE_REASON_DEPENDENT) {
+ notifyDependents(key, mutableSetOf())
+ }
+ }
+
+ /** Notifies dependents recursively. */
+ private fun notifyDependents(key: String, notifiedKeys: MutableSet<String>) {
+ if (!notifiedKeys.add(key)) return
+ for (dependency in dependencies[key]) {
+ notifyChange(dependency, CHANGE_REASON_DEPENDENT)
+ notifyDependents(dependency, notifiedKeys)
+ }
+ }
+
+ override fun close() {
+ removeObserver(preferenceObserver)
+ val context = preferenceScreen.context
+ for (preference in preferences.values) {
+ if (preference is PreferenceLifecycleProvider) preference.onDetach(context)
+ }
+ for (storage in storages) storage.removeObserver(storageObserver)
+ }
+
+ companion object {
+ /** Preference value is changed. */
+ private const val CHANGE_REASON_VALUE = 0
+ /** Preference state (title/summary, enable state, etc.) is changed. */
+ private const val CHANGE_REASON_STATE = 1
+ /** Dependent preference state is changed. */
+ private const val CHANGE_REASON_DEPENDENT = 2
+
+ /** Updates preference screen that has incomplete hierarchy. */
+ @JvmStatic
+ fun bind(preferenceScreen: PreferenceScreen) {
+ PreferenceScreenRegistry[preferenceScreen.key]?.run {
+ if (!hasCompleteHierarchy()) {
+ val preferenceBindingFactory =
+ (this as? PreferenceScreenCreator)?.preferenceBindingFactory ?: return
+ bindRecursively(
+ preferenceScreen,
+ preferenceBindingFactory,
+ getPreferenceHierarchy(preferenceScreen.context),
+ )
+ }
+ }
+ }
+
+ internal fun bindRecursively(
+ preferenceScreen: PreferenceScreen,
+ preferenceBindingFactory: PreferenceBindingFactory,
+ preferenceHierarchy: PreferenceHierarchy,
+ ) =
+ preferenceScreen.bindRecursively(
+ preferenceBindingFactory,
+ preferenceHierarchy.getAllPreferences().associateBy { it.key },
+ )
+
+ private fun PreferenceGroup.bindRecursively(
+ preferenceBindingFactory: PreferenceBindingFactory,
+ preferences: Map<String, PreferenceMetadata>,
+ ) {
+ preferenceBindingFactory.bind(this, preferences[key])
+ val count = preferenceCount
+ for (index in 0 until count) {
+ val preference = getPreference(index)
+ if (preference is PreferenceGroup) {
+ preference.bindRecursively(preferenceBindingFactory, preferences)
+ } else {
+ preferenceBindingFactory.bind(preference, preferences[preference.key])
+ }
+ }
+ }
+
+ private fun PreferenceBindingFactory.bind(
+ preference: Preference,
+ metadata: PreferenceMetadata?,
+ ) = metadata?.let { getPreferenceBinding(it)?.bind(preference, it) }
+ }
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
new file mode 100644
index 0000000..7f99d7a
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.preference
+
+import android.content.Context
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.PreferenceManager
+import androidx.preference.PreferenceScreen
+import com.android.settingslib.metadata.PreferenceScreenRegistry
+
+/** Factory to create preference screen. */
+class PreferenceScreenFactory {
+ /** Preference manager to create/inflate preference screen. */
+ val preferenceManager: PreferenceManager
+
+ /**
+ * Optional existing hierarchy to merge the new hierarchies into.
+ *
+ * Provide existing hierarchy will preserve the internal state (e.g. scrollbar position) for
+ * [PreferenceFragmentCompat].
+ */
+ private val rootScreen: PreferenceScreen?
+
+ /**
+ * Factory constructor from preference fragment.
+ *
+ * The fragment must be within a valid lifecycle.
+ */
+ constructor(preferenceFragment: PreferenceFragmentCompat) {
+ preferenceManager = preferenceFragment.preferenceManager
+ rootScreen = preferenceFragment.preferenceScreen
+ }
+
+ /** Factory constructor from [Context]. */
+ constructor(context: Context) : this(PreferenceManager(context))
+
+ /** Factory constructor from [PreferenceManager]. */
+ constructor(preferenceManager: PreferenceManager) {
+ this.preferenceManager = preferenceManager
+ rootScreen = null
+ }
+
+ /** Context of the factory to create preference screen. */
+ val context: Context
+ get() = preferenceManager.context
+
+ /** Returns the existing hierarchy or create a new empty preference screen. */
+ fun getOrCreatePreferenceScreen(): PreferenceScreen =
+ rootScreen ?: preferenceManager.createPreferenceScreen(context)
+
+ /**
+ * Inflates [PreferenceScreen] from xml resource.
+ *
+ * @param xmlRes The resource ID of the XML to inflate
+ * @return The root hierarchy (if one was not provided, the new hierarchy's root)
+ */
+ fun inflate(xmlRes: Int): PreferenceScreen? =
+ if (xmlRes != 0) {
+ preferenceManager.inflateFromResource(preferenceManager.context, xmlRes, rootScreen)
+ } else {
+ rootScreen
+ }
+
+ /**
+ * Creates [PreferenceScreen] of given key.
+ *
+ * The screen must be registered in [PreferenceScreenFactory] and provide a complete hierarchy.
+ */
+ fun createBindingScreen(screenKey: String?): PreferenceScreen? {
+ val metadata = PreferenceScreenRegistry[screenKey] ?: return null
+ if (metadata is PreferenceScreenCreator && metadata.hasCompleteHierarchy()) {
+ return metadata.createPreferenceScreen(this)
+ }
+ return null
+ }
+
+ companion object {
+ /** Creates [PreferenceScreen] from [PreferenceScreenRegistry]. */
+ @JvmStatic
+ fun createBindingScreen(preference: Preference): PreferenceScreen? {
+ val preferenceScreenCreator =
+ (PreferenceScreenRegistry[preference.key] as? PreferenceScreenCreator)
+ ?: return null
+ if (!preferenceScreenCreator.hasCompleteHierarchy()) return null
+ val factory = PreferenceScreenFactory(preference.context)
+ val preferenceScreen = preferenceScreenCreator.createPreferenceScreen(factory)
+ factory.preferenceManager.setPreferences(preferenceScreen)
+ return preferenceScreen
+ }
+ }
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenProvider.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenProvider.kt
new file mode 100644
index 0000000..0573292
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenProvider.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.preference
+
+import android.content.Context
+import androidx.preference.PreferenceScreen
+
+/**
+ * Interface to provide [PreferenceScreen].
+ *
+ * When implemented by Activity/Fragment, the Activity/Fragment [Context] APIs (e.g. `getContext()`,
+ * `getActivity()`) MUST not be used: preference screen creation could happen in background service,
+ * where the Activity/Fragment lifecycle callbacks (`onCreate`, `onDestroy`, etc.) are not invoked
+ * and context APIs return null.
+ */
+interface PreferenceScreenProvider {
+
+ /**
+ * Creates [PreferenceScreen].
+ *
+ * Preference screen creation could happen in background service. The implementation MUST use
+ * [PreferenceScreenFactory.context] to obtain context.
+ */
+ fun createPreferenceScreen(factory: PreferenceScreenFactory): PreferenceScreen?
+}
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index a543450..3011ce0 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0-beta07"
+ extra["jetpackComposeVersion"] = "1.7.0-rc01"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 3507605..d01c0b9 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.5.2"
+agp = "8.6.0"
compose-compiler = "1.5.11"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip
new file mode 100644
index 0000000..50432f3
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
deleted file mode 100644
index 9a97e46..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index 2c35211..a4b76b9 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 9f29c77..9a7f4b6 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=gradle-8.9-bin.zip
+distributionUrl=gradle-8.10-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index e9153e3..f0c2ea6 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -54,13 +54,13 @@
dependencies {
api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0")
- api("androidx.compose.material3:material3:1.3.0-beta05")
+ api("androidx.compose.material3:material3:1.3.0-rc01")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.0-beta07")
+ api("androidx.navigation:navigation-compose:2.8.0-rc01")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.11.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 90cee16..1f3e2425 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -25,6 +25,13 @@
val paddingLarge = 16.dp
val paddingExtraLarge = 24.dp
+ val spinnerHorizontalPadding = paddingExtraLarge
+ val spinnerVerticalPadding = paddingLarge
+
+ val actionIconWidth = 32.dp
+ val actionIconHeight = 40.dp
+ val actionIconPadding = 4.dp
+
val itemIconSize = 24.dp
val itemIconContainerSize = 72.dp
val itemPaddingStart = paddingExtraLarge
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/preference/MainSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
index 0a469b8..b28e88e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
@@ -27,6 +28,7 @@
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsShape
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
import com.android.settingslib.spa.framework.util.EntryHighlight
@Composable
@@ -38,16 +40,17 @@
true -> MaterialTheme.colorScheme.primaryContainer
else -> MaterialTheme.colorScheme.secondaryContainer
},
- shape = SettingsShape.CornerExtraLarge,
+ shape = if (isSpaExpressiveEnabled) CircleShape
+ else SettingsShape.CornerExtraLarge,
) {
InternalSwitchPreference(
title = model.title,
checked = model.checked(),
changeable = model.changeable(),
onCheckedChange = model.onCheckedChange,
- paddingStart = 20.dp,
+ paddingStart = if (isSpaExpressiveEnabled) 32.dp else 20.dp,
paddingEnd = 20.dp,
- paddingVertical = 24.dp,
+ paddingVertical = if (isSpaExpressiveEnabled) 16.dp else 24.dp,
)
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
index aceb545..62af08e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
@@ -117,7 +117,7 @@
val indication = LocalIndication.current
val onChangeWithLog = wrapOnSwitchWithLog(onCheckedChange)
val interactionSource = remember { MutableInteractionSource() }
- val modifier = remember(checked, changeable) {
+ val modifier =
if (checked != null && onChangeWithLog != null) {
Modifier.toggleable(
value = checked,
@@ -128,7 +128,6 @@
onValueChange = onChangeWithLog,
)
} else Modifier
- }
BasePreference(
title = title,
summary = summary,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index 5f320f7..9bbc16d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -17,15 +17,24 @@
package com.android.settingslib.spa.widget.scaffold
import androidx.appcompat.R
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.FindInPage
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import com.android.settingslib.spa.framework.compose.LocalNavController
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
/** Action that navigates back to last page. */
@Composable
@@ -50,6 +59,11 @@
Icon(
imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = contentDescription,
+ modifier = if (isSpaExpressiveEnabled) Modifier
+ .size(SettingsDimension.actionIconWidth, SettingsDimension.actionIconHeight)
+ .clip(SettingsShape.CornerExtraLarge)
+ .background(MaterialTheme.colorScheme.onSurfaceVariant)
+ .padding(SettingsDimension.actionIconPadding) else Modifier
)
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index 4cf741e..7d8ee79 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -39,6 +39,7 @@
import com.android.settingslib.spa.framework.compose.horizontalValues
import com.android.settingslib.spa.framework.compose.verticalValues
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
import com.android.settingslib.spa.framework.theme.settingsBackground
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -55,6 +56,10 @@
) {
ActivityTitle(title)
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
+ if (isSpaExpressiveEnabled) {
+ LaunchedEffect(scrollBehavior.state.heightOffsetLimit) { scrollBehavior.collapse() }
+ }
+
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { SettingsTopAppBar(title, scrollBehavior, actions) },
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
index c48a147..6b2db90 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -46,6 +46,7 @@
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
data class SpinnerOption(
val id: Int,
@@ -70,7 +71,10 @@
)
.selectableGroup(),
) {
- val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
+ val contentPadding = if (isSpaExpressiveEnabled) PaddingValues(
+ horizontal = SettingsDimension.spinnerHorizontalPadding,
+ vertical = SettingsDimension.spinnerVerticalPadding
+ ) else PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
Button(
modifier = Modifier.semantics { role = Role.DropdownList },
onClick = { expanded = true },
@@ -129,7 +133,11 @@
text = option?.text ?: "",
modifier = modifier
.padding(end = SettingsDimension.itemPaddingEnd)
- .padding(vertical = SettingsDimension.itemPaddingAround),
+ .then(
+ if (!isSpaExpressiveEnabled)
+ Modifier.padding(vertical = SettingsDimension.itemPaddingAround)
+ else Modifier
+ ),
color = color,
style = MaterialTheme.typography.labelLarge,
)
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/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 2b3862f..34b597b 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -129,3 +129,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "member_device_lea_active_state_sync_fix"
+ namespace: "cross_device_experiences"
+ description: "Gates whether to enable fix for member device active state sync on lea profile"
+ bug: "364201289"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/layout/zen_mode_condition.xml b/packages/SettingsLib/res/layout/zen_mode_condition.xml
index 3222174..805c81f 100644
--- a/packages/SettingsLib/res/layout/zen_mode_condition.xml
+++ b/packages/SettingsLib/res/layout/zen_mode_condition.xml
@@ -52,6 +52,7 @@
android:ellipsize="end"
android:textAlignment="viewStart"
android:maxLines="1"
+ android:scrollbars="none"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"/>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 1161a30..9c75556 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wekkers en onthounotas"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Laat hierdie app toe om wekkers te stel en tydsensitiewe handelinge te skeduleer. Dit laat die app op die agtergrond werk, wat meer batterykrag kan gebruik.\n\nAs hierdie toestemming af is, sal bestaande wekkers en tydgegronde geleenthede wat deur hierdie app geskeduleer is, nie werk nie."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"skedule, wekker, onthounota, horlosie"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Moenie Steur Nie"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Skakel aan"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Skakel Moenie steur nie aan"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 8aae837..093fbbb 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ማንቂያዎች እና አስታዋሾች"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ይህ መተግበሪያ ማንቂያዎችን እንዲያቀናብር እና የጊዜ ትብነት ያላቸው እርምጃዎችን መርሐግብር እንዲያስይዝ ይፍቀዱለት። ይህ መተግበሪያው ከበስተጀርባ ማሄድ እንዲችል ያስችለዋል፣ ይህም የበለጠ ባትሪ ሊጠቀም ይችላል።\n\nይህ ፈቃድ ከጠፋ በዚህ መተግበሪያ መርሐግብር የተያዘላቸው ነባር ማንቂያዎች እና ጊዜ-ተኮር ክስተቶች አይሰሩም።"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"የጊዜ መርሐግብር፣ ማንቂያ፣ አስታዋሽ ሰዓት"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"አይረብሹ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"አብራ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"አትረብሽን አብራ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 02fa9aa..a1f4109 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"المنبّهات والتذكيرات"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"يمكنك السماح لهذا التطبيق بضبط المنبّهات وجدولة الإجراءات لتنفيذها في الوقت المناسب. ويسمح هذا الإذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من البطارية.\n\nفي حال عدم تفعيل هذا الإذن، لن تعمل المنبهات المضبوطة والأحداث المستندة إلى الوقت المجدولة حاليًا في هذا التطبيق."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"جدول زمني، جدولة، منبّه، تذكير، ساعة"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"وضع \"عدم الإزعاج\""</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"تفعيل"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"تفعيل ميزة \"عدم الإزعاج\""</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index f7c68a3..019fb86 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"এই এপ্টোক এলাৰ্ম ছেট কৰিবলৈ আৰু সময় সংবেদনশীল কাৰ্যৰ সময়সূচী নিৰ্ধাৰণ কৰিবলৈ দিয়ক। ই এপ্টোক নেপথ্যত চলি থকাৰ অনুমতি দিয়ে যাৰ ফলত অধিক বেটাৰী ব্যৱহাৰ হয়।\n\nএই অনুমতিটো অফ কৰা থাকিলে, ইতিমধ্যে ছেট কৰা এলাৰ্ম আৰু এই এপ্টোৱে সময়সূচী নিৰ্ধাৰণ কৰা সময় ভিত্তিক অনুষ্ঠানসমূহে কাম নকৰা হ’ব।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"সময়সূচী, এলাৰ্ম, ৰিমাইণ্ডাৰ, ঘড়ী"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"অসুবিধা নিদিব"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"অন কৰক"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"অসুবিধা নিদিব অন কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 4a08007..7f17e79 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Siqnallar və xatırlatmalar"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu tətbiqə siqnallar ayarlamağa və vaxta əsaslanan əməliyyatları planlaşdırmağa icazə verin. Bu, tətbiqin arxa fonda işləməsinə imkan verir ki, nəticədə daha çox enerji istifadə edilə bilər.\n\nBu icazə deaktiv olsa, bu tətbiq tərəfindən planlaşdırılan mövcud siqnallar və vaxta əsaslanan tədbirlər işləməyəcəkdir."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"cədvəl, siqnal, xatırlatma, saat"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Narahat etməyin"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktiv edin"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Narahat Etməyin\" rejimini aktiv edin"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 617533f..25bf587 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsetnici"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Omogućite ovoj aplikaciji da podešava alarme i zakazuje vremenski osetljive radnje. To omogućava da aplikacija bude pokrenuta u pozadini, što može da troši više baterije.\n\nAko je ova dozvola isključena, postojeći alarmi i događaji zasnovani na vremenu zakazani pomoću ove aplikacije neće raditi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"zakazati, alarm, podsetnik, sat"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne uznemiravaj"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključite režim Ne uznemiravaj"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index f5c4440..3a61980 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будзільнікі і напаміны"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дазвольце гэтай праграме ўключаць будзільнікі і задаваць час дзеянняў. З такім дазволам праграма можа працаваць у фонавым рэжыме і ў выніку хутчэй разраджаць акумулятар.\n\nКалі вы не ўключыце гэты дазвол, існуючыя будзільнікі і запланаваны праграмай час падзей не будуць працаваць."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"расклад, будзільнік, напамін, гадзіннік"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Не турбаваць"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Уключыць"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Уключэнне рэжыму \"Не турбаваць\""</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index ecf1e5a..5f0def5 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будилници и напомняния"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Разрешаване на това приложение да задава будилници и да насрочва действия, ограничени във времето. Това му позволява да работи на заден план, при което може да се използва повече батерия.\n\nАко разрешението е изключено, съществуващите будилници и събитията въз основа на времето, насрочени от приложението, няма да работят."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"график, будилник, напомняне, часовник"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Не безпокойте"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Включване"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Включване на режима „Не безпокойте“"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index b6fde48..0c0b569 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"অ্যালার্ম এবং রিমাইন্ডার"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"অ্যালার্ম এবং সময়ের মধ্যে শেষ করতে হবে এমন অ্যাকশনের শিডিউল সেট করতে এই অ্যাপকে অনুমতি দিন। এর ফলে ব্যাকগ্রাউন্ডে অ্যাপ চলতে পারে, যার জন্য আরও ব্যাটারির চার্জ খরচ হতে পারে।\n\nএই অনুমতি বন্ধ করা থাকলে, আগে থেকে থাকা অ্যালার্ম এবং অ্যাপের মাধ্যমে শিডিউল করা সময় ভিত্তিক ইভেন্টের রিমাইন্ডার কাজ করবে না।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"শিডিউল, অ্যালার্ম, রিমাইন্ডার, ঘড়ি"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"বিরক্ত করবে না"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"চালু করুন"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 44391d5..ea4150f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsjetnici"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dozvolite ovoj aplikaciji da postavlja alarme i zakazuje vremenski osjetljive radnje. Ovim će se omogućiti aplikaciji da radi u pozadini, čime se može povećati potrošnja baterije.\n\nAko je ovo odobrenje isključeno, postojeći alarmi i događaji zasnovani na vremenu, a koje je ova aplikacija zakazala, neće funkcionirati."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"raspored, alarm, podsjetnik, sat"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne ometaj"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključi način rada Ne ometaj"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 9079c73..adfa6f2 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes i recordatoris"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permet que aquesta aplicació configuri alarmes i programi accions a una hora determinada. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments que ja hagi programat l\'aplicació no funcionaran."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programació, alarma, recordatori, rellotge"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"No molestis"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activa"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activa el mode No molestis"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 33955de..96d04a8 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Budíky a připomenutí"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Když tuto možnost povolíte, aplikace bude moci nastavovat budíky a plánovat akce závislé na čase. Aplikace poběží na pozadí, což může vést k vyšší spotřebě baterie.\n\nPokud toto oprávnění zůstane vypnuté, stávající budíky a události závislé na čase naplánované touto aplikací nebudou fungovat."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plán, budík, připomenutí, hodiny"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Nerušit"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Zapnout"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zapněte funkci Nerušit"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index de09e95..c6a5a43 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmer og påmindelser"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tillad, at denne app indstiller alarmer og planlægger tidsbestemte handlinger. Appen vil køre i baggrunden, hvor den muligvis bruger mere batteri.\n\nHvis denne tilladelse er deaktiveret, vil eksisterende alarmer og tidsbestemte handlinger, der er planlagt af denne app, ikke fungere."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planlæg, alarm, påmindelse, ur"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Forstyr ikke"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivér"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivér Forstyr ikke"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index f24fb35..88db823 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wecker und Erinnerungen"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dieser App erlauben, Wecker zu stellen und zeitgebundene Aktionen zu planen. Dadurch läuft die App im Hintergrund. Dies kann den Akkuverbrauch erhöhen. \n\nWenn diese Berechtigung deaktiviert ist, funktionieren bereits gestellte Wecker und zeitgebundene Ereignisse, die von dieser App geplant sind, nicht wie erwartet."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planen, Wecker, Erinnerung, Uhr"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Bitte nicht stören"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivieren"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„Bitte nicht stören“ aktivieren"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index bb518d7..35734a9 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ξυπνητήρια και υπενθυμίσεις"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Επιτρέψτε σε αυτή την εφαρμογή να ορίζει ξυπνητήρια και να προγραμματίζει ενέργειες που εξαρτώνται από τον χρόνο. Αυτό επιτρέπει στην εφαρμογή να εκτελείται στο παρασκήνιο και, ως εκ τούτου, μπορεί να καταναλώνει περισσότερη μπαταρία.\n\nΑν αυτή η άδεια δεν είναι ενεργή, τα υπάρχοντα ξυπνητήρια και συμβάντα βάσει χρόνου που έχουν προγραμματιστεί από αυτή την εφαρμογή δεν θα λειτουργούν."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"πρόγραμμα, ξυπνητήρι, υπενθύμιση, ρολόι"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Μην ενοχλείτε"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ενεργοποίηση"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ενεργοποίηση λειτουργίας \"Μην ενοχλείτε\""</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 73502ed..2d03437 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index c0b0dff..be5dfaa 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -564,6 +564,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms & reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Do Not Disturb"</string>
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 73502ed..2d03437 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 73502ed..2d03437 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 59be4be..92af284 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -564,6 +564,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms & reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Do Not Disturb"</string>
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 2cfd356..ff69f64 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta app establezca alarmas y programe acciones para horarios específicos. De esta manera, la app puede ejecutarse en segundo plano, lo que podría aumentar el consumo de batería.\n\nSi se desactiva este permiso, no funcionarán las alarmas ni los eventos basados en el tiempo existentes que programe esta app."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"No interrumpir"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar No interrumpir"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 6779f46..e2d3589 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación programe alarmas y otras acciones que se llevan a cabo a una hora determinada. Esto hace que la aplicación pueda seguir activa en segundo plano, lo que puede usar más batería.\n\nSi este permiso está desactivado, no funcionarán las alarmas ni los eventos que se activan a una hora determinada que programe esta aplicación."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"No molestar"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar el modo No molestar"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 4cb05ba..e4490a3 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmid ja meeldetuletused"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata alarme ja ajastada ajakriitilisi toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad alarmid ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajakava, äratus, meeldetuletus, kell"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Mitte segada"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Lülita sisse"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Valiku Mitte segada sisselülitamine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 666af7c..828831e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmak eta abisuak"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nBaimen hori ematen ez baduzu, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programazioa, alarma, abisua, erlojua"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ez molestatzeko modua"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktibatu"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktibatu ez molestatzeko modua"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index f2a2bb5..7858cc8 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"زنگهای ساعت و یادآوریها"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"به این برنامه اجازه میدهد زنگ ساعت تنظیم کند و کنشهای حساس به زمان را زمانبندی کند. این تنظیم به برنامه اجازه میدهد در پسزمینه اجرا شود که ممکن است باتری بیشتری مصرف کند.\n\nاگر این اجازه خاموش باشد، زنگهای ساعت موجود و رویدادهای زمانمحور که این برنامه زمانبندی کرده است کار نخواهند کرد."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"زمانبندی، زنگ ساعت، یادآوری، ساعت"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"مزاحم نشوید"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"روشن کردن"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"روشن کردن «مزاحم نشوید»"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 66db8f7..8ad343a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Herätykset ja muistutukset"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Anna sovelluksen lisätä herätyksiä ja ajoittaa kiireellisiä tapahtumia. Näin sovellus voi toimia taustalla, mikä voi kuluttaa enemmän virtaa.\n\nIlman tätä lupaa sovelluksen ajoittamat herätykset ja aikaan perustuvat tapahtumat eivät toimi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajoitus, herätys, muistutus, kello"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Älä häiritse"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ota käyttöön"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Laita Älä häiritse ‑tila päälle"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 6ce0e82..2e3a6a7 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes et rappels"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autorisez cette appli à créer des alarmes et à programmer des actions urgentes. Cela permet à l’appli de s\'exécuter en arrière-plan, ce qui peut nécessiter plus de pile.\n\nSi cette autorisation est désactivée, les alarmes existantes et les événements en temps réel programmés par cette appli ne fonctionneront pas."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"horaire, alarme, rappel, horloge"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne pas déranger"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index ea24b23..4327708 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes et rappels"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autoriser cette appli à définir des alarmes et à programmer des actions à certaines heures. Elle s\'exécutera alors en arrière-plan, ce qui peut solliciter davantage la batterie.\n\nSi l\'autorisation est désactivée, les alarmes existantes et les événements programmés par l\'appli ne fonctionneront pas."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"définir, alarme, rappel, horloge"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne pas déranger"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 647f6dd..9bcdc97 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas e recordatorios"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación defina alarmas e planifique accións que dependan da hora. Con este permiso, a aplicación pode executarse en segundo plano, o que pode provocar un maior consumo de batería.\n\nSe este permiso está desactivado, non funcionarán as alarmas que xa se definisen nin os eventos que dependan da hora planificados por esta aplicación."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planificar, alarma, recordatorio, reloxo"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Non molestar"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar modo Non molestar"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index f739410..e402ad3 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -130,7 +130,7 @@
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"સંપર્કો અને કૉલ ઇતિહાસ ઍક્સેસ કરવા દો"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"માહિતીનો ઉપયોગ કૉલની ઘોષણાઓ અને વધુ બાબતો માટે કરવામાં આવશે"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ઇન્ટરનેટ કનેક્શન શેરિંગ"</string>
- <string name="bluetooth_profile_map" msgid="8907204701162107271">"ટેક્સ્ટ સંદેશા"</string>
+ <string name="bluetooth_profile_map" msgid="8907204701162107271">"ટેક્સ્ટ મેસેજ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"સિમ ઍક્સેસ"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ઑડિયો: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ઑડિયો"</string>
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"અલાર્મ અને રિમાઇન્ડર"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"આ ઍપને અલાર્મ સેટ કરવા અને સમય પ્રતિ સંવેદનશીલ ક્રિયાઓ શેડ્યૂલ કરવા માટે મંજૂરી આપો. આ ઍપને બૅકગ્રાઉન્ડમાં ચાલવા દે છે, જેને કારણે બૅટરીનો વધુ વપરાશ થઈ શકે છે.\n\nજો આ પરવાનગી બંધ હોય, તો આ ઍપ દ્વારા શેડ્યૂલ કરવામાં આવેલા વર્તમાન અલાર્મ અને સમય આધારિત ઇવેન્ટ કામ કરશે નહીં."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"શેડ્યૂલ, અલાર્મ, રિમાઇન્ડર, ઘડિયાળ"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"ખલેલ પાડશો નહીં"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ચાલુ કરો"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ખલેલ પાડશો નહીં ચાલુ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index cdab138..2e484e4 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म और रिमाइंडर"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"इस ऐप्लिकेशन को अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर सेट करने की अनुमति दें. ऐसा करने से, ऐप्लिकेशन को बैकग्राउंड में चलने की अनुमति मिलती है. इससे बैटरी ज़्यादा खर्च होती है.\n\nऐप्लिकेशन को यह अनुमति न देने पर, इसकी मदद से सेट किए गए अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर काम नहीं करेंगे."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्यूल, अलार्म, रिमाइंडर, घड़ी"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"परेशान न करें"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"चालू करें"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'परेशान न करें\' चालू करें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index fbf89cb..a1c9a61 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsjetnici"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Omogućite toj aplikaciji da postavlja alarme i zakazuje radnje u točno određeno vrijeme. To aplikaciji omogućuje da se izvodi u pozadini, pa je moguća dodatna potrošnja baterije.\n\nAko je to dopuštenje isključeno, postojeći alarmi i događaji zakazani putem te aplikacije neće funkcionirati."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"raspored, alarm, podsjetnik, sat"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne uznemiravaj"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključite opciju Ne uznemiravaj."</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 541406d..5ea4fdb 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ébresztések és emlékeztetők"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lehetővé teszi ennek az alkalmazásnak, hogy ébresztéseket állítson be és időérzékeny feladatokat ütemezzen. Ezzel engedélyezi az alkalmazásnak, hogy a háttérben fusson, ami megnövekedett akkumulátorhasználattal járhat.\n\nHa ez az engedély ki van kapcsolva, az alkalmazás által beállított ébresztések és ütemezett időérzékeny események nem fognak működni."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ütemezés, ébresztés, emlékeztető, óra"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne zavarjanak"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Bekapcsolás"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"A Ne zavarjanak mód bekapcsolása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index ac77232..f7b51a5 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Զարթուցիչներ և հիշեցումներ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Թույլատրել այս հավելվածին զարթուցիչներ կարգավորել և որոշակի ժամանակի համար գործողություններ պլանավորել։ Այդ դեպքում հավելվածն աշխատում է ֆոնային ռեժիմում, և արդյունքում մարտկոցի լիցքն ավելի արագ է սպառվում։\n\nԵթե այս թույլտվությունն անջատված է, հավելվածի կողմից կարգավորված զարթուցիչները և գործողությունների ժամանակացույցները չեն աշխատի։"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ժամանակացույց, զարթուցիչ, հիշեցում, ժամացույց"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Չանհանգստացնել"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Միացնել"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Միացրեք «Չանհանգստացնել» ռեժիմը"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 2297376..070c161 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -432,7 +432,7 @@
<string name="force_allow_on_external_summary" msgid="8525425782530728238">"Membuat semua aplikasi dapat ditulis ke penyimpanan eksternal, terlepas dari nilai manifes"</string>
<string name="force_resizable_activities" msgid="7143612144399959606">"Paksa aktivitas agar ukurannya dapat diubah"</string>
<string name="force_resizable_activities_summary" msgid="2490382056981583062">"Membuat semua aktivitas dapat diubah ukurannya untuk banyak jendela, terlepas dari nilai manifes."</string>
- <string name="enable_freeform_support" msgid="7599125687603914253">"Aktifkan jendela berformat bebas"</string>
+ <string name="enable_freeform_support" msgid="7599125687603914253">"Aktifkan jendela berbentuk bebas"</string>
<string name="local_backup_password_title" msgid="4631017948933578709">"Sandi cadangan desktop"</string>
<string name="local_backup_password_summary_none" msgid="7646898032616361714">"Saat ini cadangan desktop penuh tidak dilindungi"</string>
<string name="local_backup_password_summary_change" msgid="1707357670383995567">"Ketuk guna mengubah atau menghapus sandi untuk cadangan lengkap desktop"</string>
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarm & pengingat"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Mengizinkan aplikasi ini menyetel alarm dan menjadwalkan tindakan yang sensitif waktu. Hal ini memungkinkan aplikasi berjalan di latar belakang, sehingga mungkin menggunakan lebih banyak daya baterai.\n\nJika izin ini dinonaktifkan, alarm dan acara berbasis waktu yang dijadwalkan oleh aplikasi ini tidak akan berfungsi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadwal, alarm, pengingat, jam"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Jangan Ganggu"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktifkan"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktifkan mode Jangan Ganggu"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 30d36bc..5408b50 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Vekjarar og áminningar"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leyfa þessu forriti að stilla vekjara og áætla aðgerðir sem þurfa að eiga sér stað innan ákveðins tímaramma. Þetta leyfir forritinu að keyra í bakgrunninum sem getur notað meiri rafhlöðuorku.\n\nEf slökkt er á þessari heimild munu núverandi vekjarar og tímasettir viðburðir sem þetta forrit stillir ekki virka."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"áætlun, vekjari, áminning, klukka"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ónáðið ekki"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Kveikja"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Kveikja á „Ónáðið ekki“"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 9dac1f6..10394f1 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Sveglie e promemoria"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Consenti a questa app di impostare sveglie e programmare azioni per orari specifici. L\'app potrà essere eseguita in background, comportando un consumo maggiore della batteria.\n\nSe questa autorizzazione viene disattivata, le sveglie esistenti e gli eventi basati sull\'orario programmati da questa app non funzioneranno."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programmare, sveglia, promemoria, orologio"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Non disturbare"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Attiva"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Attiva Non disturbare"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index e7ad1eb..fce947a 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"שעונים מעוררים ותזכורות"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ההרשאה הזו מתירה לאפליקציה להגדיר שעון מעורר ולתזמן פעולות דחופות. האפליקציה תוכל לפעול ברקע ובכך להגביר את צריכת הסוללה.\n\nאם ההרשאה מושבתת, ההתראות והאירועים מבוססי-הזמן שהוגדרו ותוזמנו על ידי האפליקציה לא יפעלו."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"תזמון, שעון מעורר, תזכורת, שעון"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"נא לא להפריע"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"הפעלה"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"הפעלת מצב נא לא להפריע"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 9848a91..41acfc0 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"アラームとリマインダー"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"アラームの設定や時間ベースのアクション設定を、このアプリに許可します。これによりアプリがバックグラウンドで実行できるようになるため、バッテリーの使用量が増えることがあります。\n\nこの権限が OFF の場合、このアプリで設定された既存のアラームと時間ベースのイベントは機能しなくなります。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"スケジュール, アラーム, リマインダー, 時計"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"サイレント モード"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ON にする"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"サイレント モードを ON にする"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 3c03b3c..58a01a4 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"მაღვიძარები და შეხსენებები"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ნებას რთავს ამ აპს, დააყენოს მაღვიძარები და დაგეგმოს დროზე დამოკიდებული მოქმედებები. ეს საშუალებას აძლევს აპს, იმუშაოს ფონურად, რამაც შეიძლება ბატარეის ხარჯი გაზარდოს.\n\nთუ ეს ნებართვა გამორთულია, ამ აპით დაგეგმილი მაღვიძარები და დროზე დამოკიდებული მოვლენები არ იმუშავებს."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"განრიგი, მაღვიძარა, შეხსენება, საათი"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"არ შემაწუხოთ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ჩართვა"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„არ შემაწუხოთ“ რეჟიმის ჩართვა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 80a1442..485120d 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Оятқыштар мен еске салғыштар"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бұл қолданбаға оятқыштарды орнатуға және уақытқа байланысты әрекеттерді жоспарлауға рұқсат береді. Мұндайда қолданба фондық режимде жұмыс істейді, сондықтан батарея шығыны артуы мүмкін.\n\nБұл рұқсат өшірулі болса, осы қолданбада жоспарланған ағымдағы оятқыштар мен уақытқа байланысты іс-шаралар жұмыс істемейді."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"кесте, оятқыш, еске салғыш, сағат"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Мазаламау"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Қосу"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Мазаламау режимін қосу"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 4010a8b..7fbc509 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ម៉ោងរោទ៍ និងការរំលឹក"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"អនុញ្ញាតឱ្យកម្មវិធីនេះកំណត់ម៉ោងរោទ៍ និងកំណត់កាលវិភាគសកម្មភាពដែលតម្រូវឱ្យទាន់ពេលវេលា។ ការធ្វើបែបនេះអនុញ្ញាតឱ្យកម្មវិធីនេះដំណើរការនៅផ្ទៃខាងក្រោយ ដែលអាចប្រើថ្មកាន់តែច្រើន។\n\nប្រសិនបើបិទការអនុញ្ញាតនេះ ម៉ោងរោទ៍ដែលមានស្រាប់ និងព្រឹត្តិការណ៍ផ្អែកលើពេលវេលាដែលកំណត់ដោយកម្មវិធីនេះនឹងមិនដំណើរការទេ។"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"កាលវិភាគ ម៉ោងរោទ៍ ការរំលឹក នាឡិកា"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"កុំរំខាន"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"បើក"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"បើកមុខងារកុំរំខាន"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index ea1dc7d..85075c3 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ಅಲಾರಂಗಳು ಮತ್ತು ರಿಮೈಂಡರ್ಗಳು"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ಅಲಾರಂಗಳನ್ನು ಹೊಂದಿಸಲು ಮತ್ತು ಸಮಯ-ಸೂಕ್ಷ್ಮವಾದ ಕ್ರಿಯೆಗಳನ್ನು ನಿಗದಿಪಡಿಸಲು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ. ಇದು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಅದರಿಂದ ಹೆಚ್ಚು ಬ್ಯಾಟರಿ ಬಳಕೆಯಾಗಬಹುದು.\n\nಈ ಅನುಮತಿ ಆಫ್ ಆಗಿದ್ದರೆ, ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಲಾರಂಗಳು ಮತ್ತು ಈ ಆ್ಯಪ್ ನಿಗದಿಪಡಿಸಿದ ಸಮಯ-ಸೂಕ್ಷ್ಮ ಈವೆಂಟ್ಗಳು ಕೆಲಸ ಮಾಡುವುದಿಲ್ಲ."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ವೇಳಾಪಟ್ಟಿ, ಅಲಾರಂ, ರಿಮೈಂಡರ್, ಗಡಿಯಾರ"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ಆನ್ ಮಾಡಿ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಅನ್ನು ಆನ್ ಮಾಡಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 2a8807fe..480d78f 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"알람 및 리마인더"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"이 앱이 알람을 설정하고 시간 기반 작업을 예약할 수 있도록 허용합니다. 이렇게 하면 백그라운드에서 앱 실행이 허용되어 배터리 사용량이 증가할 수 있습니다.\n\n이 권한을 사용 중지하면 이 앱에서 예약한 기존의 알람 및 시간 기반 일정이 작동하지 않습니다."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"일정 예약, 알람, 리마인더, 시계"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"방해 금지 모드"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"사용 설정"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"방해 금지 모드 사용 설정"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 8e1a1a4..1667041 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ойготкучтар жана эстеткичтер"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бул колдонмого ойготкучтарды коюуга жана башка аракеттерди графикке киргизүүгө уруксат бересиз. Ушуну менен колдонмо фондо иштеп, батареяны көбүрөөк сарпташы мүмкүн.\n\nЭгер бул уруксат өчүрүлсө, колдонмодогу ойготкучтар жана графикке киргизилген башка аракеттер иштебейт."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"график, ойготкуч, эстеткич, саат"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Тынчымды алба"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Күйгүзүү"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Тынчымды алба\" режимин күйгүзүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 1c7f520..2c6d6ba 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ອະນຸຍາດໃຫ້ແອັບນີ້ຕັ້ງໂມງປຸກ ແລະ ກຳນົດເວລາຄຳສັ່ງທີ່ເນັ້ນເລື່ອງເວລາເປັນສຳຄັນໄດ້. ນີ້ຈະເຮັດໃຫ້ແອັບເຮັດວຽກໄດ້ໃນພື້ນຫຼັງ, ເຊິ່ງອາດໃຊ້ແບັດເຕີຣີຫຼາຍຂຶ້ນ.\n\nຫາກປິດການອະນຸຍາດນີ້ໄວ້, ໂມງປຸກທີ່ມີຢູ່ກ່ອນແລ້ວ ແລະ ເຫດການທີ່ອ້າງອີງເວລາທີ່ກຳນົດໄວ້ໂດຍແອັບນີ້ຈະບໍ່ສາມາດເຮັດວຽກໄດ້."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ກຳນົດເວລາ, ໂມງປຸກ, ການແຈ້ງເຕືອນ, ໂມງ"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"ຫ້າມລົບກວນ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ເປີດ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ເປີດໂໝດຫ້າມລົບກວນ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 35141d9..a6ef078 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signalai ir priminimai"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leiskite šiai programai nustatyti signalus ir suplanuoti veiksmus, kuriems svarbus laiko veiksnys. Dėl to programa gali veikti fone ir sunaudoti daugiau akumuliatoriaus energijos.\n\nJei šis leidimas išjungtas, šios programos suplanuoti esami signalai ir laiku pagrįsti įvykiai neveiks."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"tvarkaraštis, signalas, priminimas, laikrodis"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Netrukdymo režimas"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Įjungti"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Netrukdymo režimo įjungimas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index dae1d2f..a1fef64 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signāli un atgādinājumi"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Atļaujiet šai lietotnei iestatīt signālus un ieplānot darbības, kas jāveic konkrētā laikā. Tādējādi lietotne darbosies fonā un, iespējams, patērēs vairāk akumulatora enerģijas.\n\nJa šī atļauja nav piešķirta, esošie signāli un šīs lietotnes ieplānotie notikumi, kas jāizpilda konkrētā laikā, nedarbosies."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ieplānot, signāls, atgādinājums, pulkstenis"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Netraucēt"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ieslēgt"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Režīma “Netraucēt” ieslēgšana"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index a256fa7..e22f67c 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Аларми и потсетници"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дозволете ѝ на апликацијава да поставува аларми и да закажува дејства со временски рокови. Ова овозможува апликацијата да работи во заднина и така може повеќе да ја троши батеријата.\n\nАко дозволава е исклучена, нема да функционираат постојните аларми и настаните според време закажани од апликацијава."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"закажување, аларм, потсетник, часовник"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Не вознемирувај"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Вклучи"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Исклучување на „Не вознемирувај“"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 32a779e..0b6d3fb 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"അലാറങ്ങളും റിമെെൻഡറുകളും"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും സമയപ്രാധാന്യമുള്ള പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. പശ്ചാത്തലത്തിൽ റൺ ചെയ്യാൻ ഇത് ഈ ആപ്പിന് അനുവാദം നൽകുന്നു, ഇതിന് കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാം.\n\nഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് നിലവിൽ ഷെഡ്യൂൾ ചെയ്ത അലാറങ്ങളും സമയാധിഷ്ഠിത ഇവന്റുകളും പ്രവർത്തിക്കില്ല."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ഷെഡ്യൂൾ, അലാറം, റിമെെൻഡർ, ക്ലോക്ക്"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"ശല്യപ്പെടുത്തരുത്"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ഓണാക്കുക"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ശല്യപ്പെടുത്തരുത്\' ഓണാക്കുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 2a70e1c..1ce8b0f 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Сэрүүлэг, сануулагч"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Энэ аппад сэрүүлэг тавих болон хугацаанд мэдрэг үйлдлийн хуваарь гаргахыг зөвшөөрнө үү. Энэ нь аппад ард ажиллахыг зөвшөөрөх бөгөөд ингэснээр илүү их батарей ашиглаж магадгүй.\n\nХэрэв энэ зөвшөөрөл унтраалттай бол энэ аппын аль хэдийн тавьсан сэрүүлэг болон хуваарь гаргасан хугацаанд мэдрэг үйл явдал ажиллахгүй."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"хуваарь, сэрүүлэг, сануулагч, цаг"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Бүү саад бол"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Асаах"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Бүү саад бол горимыг асаах"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index cd32f6d..ad67ef2 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म आणि रिमाइंडर"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"या ॲपला अलार्म सेट करण्याची किंवा वेळेनुसार संवेदनशील असलेल्या कृती शेड्युल करण्याची अनुमती द्या. हे ॲपला बॅकग्राउंडमध्ये रन होऊ देते, ज्यामुळे जास्त बॅटरी वापरली जाऊ शकते.\n\nही परवानगी बंद असल्यास, सध्याचे अलार्म आणि या ॲपद्वारे शेड्युल केलेले वेळेवर आधारित इव्हेंट काम करणार नाहीत."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्युल, अलार्म, रिमाइंडर, घड्याळ"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"व्यत्यय आणू नका"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सुरू करा"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"व्यत्यय आणू नका सुरू करा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index da4e53b..5bb3ba0 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Penggera & peringatan"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Benarkan apl ini menetapkan penggera dan menjadualkan tindakan yang sensitif masa. Hal ini membolehkan apl berjalan di latar, yang mungkin menggunakan lebih banyak bateri.\n\nJika kebenaran ini dimatikan, penggera sedia ada dan acara berdasarkan masa yang dijadualkan oleh apl ini tidak akan berfungsi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadual, penggera, peringatan, jam"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Jangan Ganggu"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Hidupkan"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Hidupkan Jangan Ganggu"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index f9fae5a..7cce3d9 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"နှိုးစက်နှင့် သတိပေးချက်များ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"နှိုးစက်သတ်မှတ်ရန်နှင့် အချိန်တိကျရန် လိုအပ်သည့် လုပ်ဆောင်ချက်များအတွက် အစီအစဉ်ဆွဲရန် ဤအက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက အက်ပ်ကို နောက်ခံတွင် လုပ်ဆောင်ခွင့်ပေးပြီး ဘက်ထရီပိုသုံးနိုင်သည်။\n\nဤခွင့်ပြုချက်ကို ပိတ်ထားပါက ဤအက်ပ်ဖြင့် အစီအစဉ်ဆွဲထားသော လက်ရှိနှိုးစက်နှင့် အချိန်သတ်မှတ်ထားသည့် အစီအစဉ်များ အလုပ်လုပ်တော့မည် မဟုတ်ပါ။"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"အချိန်ဇယား၊ နှိုးစက်၊ သတိပေးချက်၊ နာရီ"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"မနှောင့်ယှက်ရ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ဖွင့်ရန်"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'မနှောင့်ယှက်ရ\' ဖွင့်ခြင်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index d221b9b..c6fbea1 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmer og påminnelser"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Gi denne appen tillatelse til å angi alarmer og planlegge tidssensitive handlinger. Dette gir appen tillatelse til å kjøre i bakgrunnen, noe som kan bruke mer batteri.\n\nHvis denne tillatelsen er av, fungerer ikke eksisterende alarmer og tidsbaserte hendelser som er planlagt av denne appen."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"tidsplan, alarm, påminnelse, klokke"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ikke forstyrr"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Slå på"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Slå på Ikke forstyrr"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 1d419ee..5d18fd3 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म तथा रिमाइन्डर"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन बनाउने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा चल्छ र धेरै ब्याट्री खपत हुन्छ।\n\nयो अनुमति दिइएको छैन भने सेट गरिएका अलार्म बज्दैनन् र यो एपले तय गरेका गतिविधि चल्दैनन्।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"समयतालिका, अलार्म, रिमाइन्डर, घडी"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"बाधा नपुऱ्याउनुहोस्"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"अन गर्नुहोस्"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"बाधा नपुऱ्याउनुहोस् नामक मोडलाई अन गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 749e5da..e457376 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wekkers en herinneringen"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Sta toe dat deze app wekkers zet en tijdgevoelige acties plant. De app kan hierdoor op de achtergrond worden uitgevoerd, waardoor je misschien meer batterijlading verbruikt.\n\nAls dit recht uitstaat, werken door deze app geplande bestaande wekkers en tijdgebaseerde afspraken niet."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plannen, schema, wekker, alarm, herinnering, klok"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Niet storen"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aanzetten"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zet Niet storen aan."</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 5ca07c0..2f25cde5 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ଏହି ଆପକୁ ଆଲାରାମ୍ ସେଟ୍ କରିବାକୁ ଏବଂ ସମୟ-ସମ୍ବେଦନଶୀଳ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଏହା ଆପକୁ ପୃଷ୍ଠପଟରେ ଚାଲିବାକୁ ଦେଇଥାଏ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ।\n\nଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଅଛି, ତେବେ ଏହି ଆପ୍ ଦ୍ୱାରା ସିଡୁଲ୍ କରାଯାଇଥିବା ପୂର୍ବରୁ ଥିବା ଆଲାରାମ୍ ଏବଂ ସମୟ-ଆଧାରିତ ଇଭେଣ୍ଟଗୁଡ଼ିକ କାମ କରିବ ନାହିଁ।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ସିଡୁଲ୍, ଆଲାରାମ୍, ରିମାଇଣ୍ଡର୍, ଘଣ୍ଟା"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ କରନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 48db7f4..b13ef1b 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਵੱਧ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਇਸ ਐਪ ਰਾਹੀਂ ਨਿਯਤ ਕੀਤੇ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ਸਮਾਂ-ਸੂਚੀ, ਅਲਾਰਮ, ਰਿਮਾਈਂਡਰ, ਘੜੀ"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ਚਾਲੂ ਕਰੋ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index d0b6979..3f90774 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmy i przypomnienia"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Zezwalaj tej aplikacji na ustawianie alarmów i planowanie działań, w przypadku których czas jest istotny. Aplikacja będzie mogła działać w tle, co może zwiększyć wykorzystanie baterii.\n\nJeśli nie włączysz tego uprawnienia, istniejące alarmy i zaplanowane wydarzenia z tej aplikacji nie będą działać."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"harmonogram, alarm, przypomnienie, zegar"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Nie przeszkadzać"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Włącz"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Włącz tryb Nie przeszkadzać"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 6eaa518..00375b0 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarme, lembrete, relógio"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Não perturbe"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o Não perturbe"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index a411dc8..97d2848 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permita que a app defina alarmes e agende ações com um horário específico. Esta ação permite que a app seja executada em segundo plano, o que pode usar mais bateria.\n\nSe esta autorização estiver desativada, os alarmes existentes e os eventos com base no tempo agendados por esta app não funcionam."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"agendar, alarme, lembrete, relógio"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Não incomodar"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o modo Não incomodar"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 6eaa518..00375b0 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarme, lembrete, relógio"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Não perturbe"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o Não perturbe"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 71f0d2f..900851e 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarme și mementouri"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite acestei aplicații să seteze alarme și să planifice acțiuni care trebuie realizate în timp scurt. Astfel, aplicația poate să ruleze în fundal, ceea ce ar putea crește consumul de baterie.\n\nDacă permisiunea este dezactivată, alarmele și evenimentele dependente de timp planificate de aplicație nu vor funcționa."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programare, alarmă, memento, ceas"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Nu deranja"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activează"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activează Nu deranja"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 0c9c9cf..db4be66 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будильники и напоминания"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Вы можете разрешить этому приложению устанавливать будильники и планировать запуск действий в определенное время. В этом случае оно будет работать в фоновом режиме и быстрее расходовать заряд батареи.\n\nЕсли отключить это разрешение, текущие будильники и созданные приложением события перестанут запускаться."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"установить, будильник, напоминание, часы"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Не беспокоить"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Включить"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Включите режим \"Не беспокоить\""</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 0bba018..d9df5eb 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"එලාම සහ සිහිකැඳවීම්"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"එලාම සැකසීමට සහ කාල සංවේදී ක්රියා කාලසටහන්ගත කිරීමට මෙම යෙදුමට ඉඩ දෙන්න. මෙය පසුබිමේ ධාවනය වීමට යෙදුමට ඉඩ දෙයි, එය වැඩි බැටරිය වැඩියෙන් භාවිත කළ හැකිය.\n\nමෙම අවසරය ක්රියාවිරහිත නම්, මෙම යෙදුම මඟින් සැලසුම් කර ඇති තිබෙන එලාම සහ වේලාව පදනම් කර ගත් සිදුවීම් ක්රියා නොකරනු ඇත."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"කාල සටහන, එලාමය, සිහිකැඳවීම, ඔරලෝසුව"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"බාධා නොකරන්න"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ක්රියාත්මක කරන්න"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"බාධා නොකරන්න ක්රියාත්මක කරන්න"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index acb528f1..c690a19 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Budíky a pripomenutia"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Povoľte tejto aplikácii nastavovať budíky a plánovať akcie s časovým obmedzením. Aplikácii to umožní pracovať na pozadí, čo môže zvýšiť spotrebu batérie.\n\nAk je toto povolenie vypnuté, existujúce budíky a udalosti s časovým obmedzením naplánované touto aplikáciou nebudú fungovať."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plán, budík, pripomenutie, hodiny"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Režim bez vyrušení"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Zapnúť"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zapnite režim bez vyrušení"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index ff11a7a..7fa5ed1 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi in opomniki"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tej aplikaciji dovolite nastavljanje alarmov in načrtovanje časovno občutljivih dejanj. S tem aplikaciji omogočite izvajanje v ozadju, kar bo morda povečalo porabo energije baterije.\n\nČe je to dovoljenje izklopljeno, obstoječi alarmi in časovno občutljivi dogodki, ki jih nastavi ta aplikacija, ne bodo delovali."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"načrtovanje, urnik, alarm, opomnik, ura"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne moti"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Vklopi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Vklop načina »Ne moti«"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index f0d1ac5..b64c8fd 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmet dhe alarmet rikujtuese"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lejo që ky aplikacion të caktojë alarmet dhe të planifikojë veprime që kanë një afat të caktuar. Kjo mundëson që aplikacioni të ekzekutohet në sfond, gjë që mund të përdorë më shumë bateri.\n\nNëse kjo leje është caktuar si joaktive, alarmet ekzistuese dhe ngjarjet në bazë kohore të planifikuara nga ky aplikacion nuk do të funksionojnë."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planifiko, alarm, alarm rikujtues, ora"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Mos shqetëso"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivizo"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivizo \"Mos shqetëso\""</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 557e95b..f9001a8 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Аларми и подсетници"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Омогућите овој апликацији да подешава аларме и заказује временски осетљиве радње. То омогућава да апликација буде покренута у позадини, што може да троши више батерије.\n\nАко је ова дозвола искључена, постојећи аларми и догађаји засновани на времену заказани помоћу ове апликације неће радити."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"заказати, аларм, подсетник, сат"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Не узнемиравај"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Укључи"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Укључите режим Не узнемиравај"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 2404dc2..4ca4835 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarm och påminnelser"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tillåt att den här appen ställer in alarm och schemalägger tidskänsliga åtgärder. Om du tillåter detta kan appen köras i bakgrunden, vilket kan dra mer batteri.\n\nOm behörigheten är inaktiverad fungerar inte befintliga alarm och tidsbaserade händelser som schemalagts av den här appen."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schema, alarm, påminnelse, klocka"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Stör ej"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivera"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivera Stör ej."</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 03ddecd..3198d0a 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Kengele na vikumbusho"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Ruhusu programu hii iweke kengele na ratiba za vitendo vingine vinavyotegemea wakati. Hatua hii inairuhusu programu itumike chinichini, hali inayoweza kutumia chaji nyingi ya betri.\n\nIkiwa ruhusa hii itazimwa, kengele zilizopo na ratiba za vitendo vinavyotegemea wakati zilizowekwa na programu hii hazitafanya kazi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ratiba, kengele, kikumbusho, saa"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Usinisumbue"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Washa"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Washa kipengele cha Usinisumbue"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index d3f3c96..108ddce 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"அலாரங்கள் & நினைவூட்டல்கள்"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"அலாரங்களை அமைக்கவும் குறிப்பிட்ட கால இடைவெளியில் செயல்களைத் திட்டமிடவும் இந்த ஆப்ஸை அனுமதிக்கும். இது ஆப்ஸ் பின்னணியில் இயங்குவதை அனுமதிக்கும், இதற்காக அதிக பேட்டரியைப் பயன்படுத்தக்கூடும்.\n\nஇந்த அனுமதி முடக்கப்பட்டிருந்தால் இந்த ஆப்ஸ் மூலம் திட்டமிடப்பட்ட ஏற்கெனவே அமைத்த அலாரங்களும் நேர அடிப்படையிலான நிகழ்வுகளும் வேலை செய்யாது."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"திட்டமிடல், அலாரம், நினைவூட்டல், கடிகாரம்"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"தொந்தரவு செய்யாதே"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ஆன் செய்"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"தொந்தரவு செய்ய வேண்டாம் என்பதை ஆன் செய்யும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 0aa94a6..4811594 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"అలారాలు & రిమైండర్లు"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"అలారాలను సెట్ చేయడానికి, టైమ్-సెన్సిటివ్ చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. ఇది యాప్ను బ్యాక్గ్రౌండ్లో రన్ అవడానికి అనుమతిస్తుంది, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు.\n\nఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ ద్వారా షెడ్యూల్ చేసినటువంటి ఇప్పటికే ఉన్న అలారాలు, టైమ్-ఆధారిత ఈవెంట్లు పనిచేయవు."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"షెడ్యూల్, అలారం, రిమైండర్, గడియారం"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"అంతరాయం కలిగించవద్దు"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ఆన్ చేయండి"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"అంతరాయం కలిగించవద్దును ఆన్ చేయండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 970e456..b332154 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"การปลุกและการช่วยเตือน"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"อนุญาตให้แอปนี้ตั้งปลุกและกำหนดเวลาการดำเนินการที่ต้องคำนึงถึงเวลาเป็นสำคัญ สิทธิ์นี้ช่วยให้แอปทำงานในเบื้องหลังได้ จึงอาจทำให้ใช้แบตเตอรี่มากขึ้น\n\nหากปิดใช้สิทธิ์นี้ การปลุกที่มีอยู่และกิจกรรมที่ต้องคำนึงถึงเวลาเป็นสำคัญซึ่งแอปนี้กำหนดเวลาไว้จะไม่ทำงาน"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"กำหนดเวลา การปลุก การช่วยเตือน นาฬิกา"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"ห้ามรบกวน"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"เปิด"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"เปิด \"ห้ามรบกวน\""</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 0ba4a7b..cf88980 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Mga alarm at paalala"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Payagan ang app na ito na magtakda ng mga alarm at mag-iskedyul ng mga pagkilos na may limitadong oras. Papayagan nitong tumakbo ang app sa background, na posibleng gumamit ng mas maraming baterya.\n\nKung naka-off ang pahintulot na ito, hindi gagana ang mga kasalukuyang alarm at event na nakabatay sa oras na naiskedyul ng app na ito."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"iskedyul, alarm, paalala, orasan"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Huwag Istorbohin"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"I-on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"I-on ang Huwag Istorbohin"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 0506480..6cdfa2e 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmlar ve hatırlatıcılar"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu uygulamanın alarm kurmasına ve zamana bağlı işlemler programlamasına izin verin. Bu izin, uygulamanın arka planda çalışmasına olanak sağlayarak daha fazla pil harcanmasına neden olabilir.\n\nBu izin verilmezse bu uygulama tarafından programlanmış mevcut alarmlar ve zamana bağlı etkinlikler çalışmaz."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"program, alarm, hatırlatıcı, saat"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Rahatsız Etmeyin"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aç"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Rahatsız Etmeyin\'i açın"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 0ee73bd..7abd4a8 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будильники й нагадування"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дозволити цьому додатку налаштовувати будильники й створювати розклад дій. Додаток зможе працювати у фоновому режимі й використовувати більше заряду акумулятора.\n\nЯкщо вимкнути такий дозвіл, наявні будильники й дії, створені цим додатком, не працюватимуть."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"запланувати, будильник, нагадування, годинник"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Не турбувати"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Увімкнути"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Увімкнути режим \"Не турбувати\""</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 3678410..bf7ac58 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"الارمز اور یاد دہانیاں"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"اس ایپ کو الارمز سیٹ کرنے اور وقت کے لحاظ سے حساس کارروائیوں کو شیڈول کرنے کی اجازت دیں۔ اس سے ایپ کو پس منظر میں چلنے کی اجازت ملتی ہے، جس میں زیادہ بیٹری استعمال ہو سکتی ہے۔\n\n اگر یہ اجازت آف ہے تو موجودہ الارمز اور اس ایپ کے ذریعے شیڈول کردہ وقت پر مبنی ایونٹس کام نہیں کریں گے۔"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"شیڈول، الارم، یاد دہانی، گھڑی"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"ڈسٹرب نہ کریں"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"آن کریں"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ڈسٹرب نہ کریں\' کو آن کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 893cd57..2418053 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signal va eslatmalar"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu ilovaga signal oʻrnatish va vaqtga asoslangan amallarni rejalashtirishga ruxsat berish. Bunda ilovaga orqa fonda ishlashiga imkon beriladi, shu sababli batareya ortiqcha sarflanishi mumkin.\n\nAgar bu ruxsat oʻchirilsa, ushbu ilova tomonidan rejalashtirilgan mavjud signallar va vaqtga asoslangan tadbirlar ishlamaydi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"reja, signal, eslatma, soat"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Bezovta qilinmasin"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Yoqish"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Bezovta qilinmasin rejimini yoqing"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 101d352..15839b8 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Chuông báo và lời nhắc"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Cho phép ứng dụng này đặt chuông báo và lên lịch các hành động cần chính xác về thời gian. Tùy chọn này cho phép ứng dụng chạy ở chế độ nền và có thể làm tiêu hao nhiều pin.\n\nNếu không cấp quyền này, các chuông báo và sự kiện theo thời gian do ứng dụng này lên lịch sẽ không hoạt động."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"lịch biểu, chuông báo, lời nhắc, đồng hồ"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Không làm phiền"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Bật"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Bật chế độ Không làm phiền"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 88e67f6..a2fd4ea 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"闹钟和提醒"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允许该应用设置闹钟以及安排在特定时间执行某些操作。这项权限开启后,该应用将在后台运行,可能会消耗更多电池电量。\n\n如果您关闭此权限,该应用设置的现有闹钟将不会响起,而且该应用安排在特定时间执行的现有活动也不会执行。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"设置, 闹钟, 提醒, 时钟, schedule, alarm, reminder, clock"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"勿扰模式"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"开启"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"开启勿扰模式"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 6a35ece..bcf624e4 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"鬧鐘和提醒"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允許此應用程式設定鬧鐘及安排具時效性的操作。這讓應用程式在背景中執行,因此可能會較耗電。\n\n如果關閉此權限,此應用程式將不會在預定時間響起已設定的鬧鐘,亦不會就特定時間的活動傳送通知。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"日程表, 鬧鐘, 提醒, 時鐘"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"請勿騷擾"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"開啟"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"開啟「請勿騷擾」模式"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 9d00a05..58a19793 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"鬧鐘和提醒"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允許這個應用程式設定鬧鐘及安排有時效性的動作。之後應用程式可以在背景執行,並可能耗用較多電量。\n\n如果關閉這項權限,這個應用程式設定的現有鬧鐘將不會響起,系統也無法在預定的時間發出活動提醒。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"時間表, 鬧鐘, 提醒, 時鐘"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"零打擾"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"開啟"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"開啟「零打擾」模式"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 0c326146..28b9b05 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -564,6 +564,8 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ama-alamu nezikhumbuzi"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Vumela le app isethe ama-alamu futhi ushejule izenzo zesikhathi esizwelayo. Lokhu kuvumela i-app iqhubeke ngemuva okungasebenzisa ibhethri lakho eliningi.\n\nUma le mvume ivaliwe, ama-alamu asele nemicimbi esekelwe esikhathini ehlelwe yile app ngeke kusebenze."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ishejuli, i-alamu, isikhumbuzi, iwashi"</string>
+ <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+ <skip />
<string name="zen_mode_settings_title" msgid="7374070457626419755">"Ungaphazamisi"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Vula"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Vula ukungaphazamisi"</string>
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index 68b81db..3c3de04 100644
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -31,4 +31,14 @@
<!-- Control whether status bar should distinguish HSPA data icon form UMTS
data icon on devices -->
<bool name="config_hspa_data_distinguishable">false</bool>
+
+ <!-- Edit User avatar explicit package name -->
+ <string name="config_avatar_picker_package" translatable="false">
+ com.android.avatarpicker
+ </string>
+
+ <!-- Edit User avatar explicit activity class -->
+ <string name="config_avatar_picker_class" translatable="false">
+ com.android.avatarpicker.ui.AvatarPickerActivity
+ </string>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 687c728..4d771c0 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1364,6 +1364,9 @@
<!-- Keywords for setting screen for controlling apps that can schedule alarms [CHAR LIMIT=100] -->
<string name="keywords_alarms_and_reminders">schedule, alarm, reminder, clock</string>
+ <!-- Priority Modes: Name of the "manual" Do Not Disturb mode. [CHAR LIMIT=50] -->
+ <string name="zen_mode_do_not_disturb_name">Do Not Disturb</string>
+
<!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]-->
<string name="zen_mode_settings_title">Do Not Disturb</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index b02b0c4..1e58335 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -495,7 +495,7 @@
|| packageName.equals(sServicesSystemSharedLibPackageName)
|| packageName.equals(sSharedSystemSharedLibPackageName)
|| packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME)
- || (updateServiceV2() && packageName.equals(getDefaultWebViewPackageName()))
+ || (updateServiceV2() && packageName.equals(getDefaultWebViewPackageName(pm)))
|| isDeviceProvisioningPackage(resources, packageName);
}
@@ -511,7 +511,7 @@
/** Fetch the package name of the default WebView provider. */
@Nullable
- private static String getDefaultWebViewPackageName() {
+ private static String getDefaultWebViewPackageName(PackageManager pm) {
if (sDefaultWebViewPackageName != null) {
return sDefaultWebViewPackageName;
}
@@ -519,9 +519,10 @@
WebViewProviderInfo provider = null;
if (android.webkit.Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager != null) {
- provider = manager.getDefaultWebViewPackage();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+ // WebViewUpdateManager.getInstance() will not return null on devices with
+ // FEATURE_WEBVIEW.
+ provider = WebViewUpdateManager.getInstance().getDefaultWebViewPackage();
}
} else {
try {
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/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingContract.kt
similarity index 72%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingContract.kt
index 3c5beeb..65adec4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingContract.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.settingslib.bluetooth.devicesettings
-parcelable BubbleBarLocation;
\ No newline at end of file
+/** The contract between the device settings provider services and Settings. */
+object DeviceSettingContract {
+ const val INVISIBLE_PROFILES = "INVISIBLE_PROFILES"
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
similarity index 84%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
index 3c5beeb..1726036 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.settingslib.bluetooth.devicesettings;
-parcelable BubbleBarLocation;
\ No newline at end of file
+parcelable DeviceSettingsProviderServiceStatus;
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt
new file mode 100644
index 0000000..977849e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * A data class representing a device settings item in bluetooth device details config.
+ *
+ * @property enabled Whether the service is enabled.
+ * @property extras Extra bundle
+ */
+data class DeviceSettingsProviderServiceStatus(
+ val enabled: Boolean,
+ val extras: Bundle = Bundle.EMPTY,
+) : Parcelable {
+
+ override fun describeContents(): Int = 0
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.run {
+ writeBoolean(enabled)
+ writeBundle(extras)
+ }
+ }
+
+ companion object {
+ @JvmField
+ val CREATOR: Parcelable.Creator<DeviceSettingsProviderServiceStatus> =
+ object : Parcelable.Creator<DeviceSettingsProviderServiceStatus> {
+ override fun createFromParcel(parcel: Parcel) =
+ parcel.run {
+ DeviceSettingsProviderServiceStatus(
+ enabled = readBoolean(),
+ extras = readBundle((Bundle::class.java.classLoader)) ?: Bundle.EMPTY,
+ )
+ }
+
+ override fun newArray(size: Int): Array<DeviceSettingsProviderServiceStatus?> {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
index d5efac9..1c0a1fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
@@ -18,10 +18,12 @@
import com.android.settingslib.bluetooth.devicesettings.DeviceInfo;
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState;
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsProviderServiceStatus;
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener;
-oneway interface IDeviceSettingsProviderService {
- void registerDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
- void unregisterDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
- void updateDeviceSettings(in DeviceInfo device, in DeviceSettingState params);
+interface IDeviceSettingsProviderService {
+ DeviceSettingsProviderServiceStatus getServiceStatus();
+ oneway void registerDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
+ oneway void unregisterDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
+ oneway void updateDeviceSettings(in DeviceInfo device, in DeviceSettingState params);
}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.kt
new file mode 100644
index 0000000..25080bc
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.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.settingslib.bluetooth.devicesettings.data.model
+
+import android.os.IInterface
+
+/** Present a service connection status. */
+sealed interface ServiceConnectionStatus<out T : IInterface> {
+ /** Service is connecting. */
+ data object Connecting : ServiceConnectionStatus<Nothing>
+
+ /** Service is connected. */
+ data class Connected<T : IInterface>(val service: T) : ServiceConnectionStatus<T>
+
+ /** Service connection failed. */
+ data object Failed : ServiceConnectionStatus<Nothing>
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
index 457d6a3..769b6e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
@@ -22,6 +22,7 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreference
import com.android.settingslib.bluetooth.devicesettings.DeviceSetting
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingContract
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
@@ -30,6 +31,9 @@
import com.android.settingslib.bluetooth.devicesettings.MultiTogglePreference
import com.android.settingslib.bluetooth.devicesettings.ToggleInfo
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel.AppProvidedItem
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel.BuiltinItem.BluetoothProfilesItem
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
@@ -103,9 +107,18 @@
private fun DeviceSettingItem.toModel(): DeviceSettingConfigItemModel {
return if (!TextUtils.isEmpty(preferenceKey)) {
- DeviceSettingConfigItemModel.BuiltinItem(settingId, preferenceKey!!)
+ if (settingId == DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES) {
+ BluetoothProfilesItem(
+ settingId,
+ preferenceKey!!,
+ extras.getStringArrayList(DeviceSettingContract.INVISIBLE_PROFILES)
+ ?: emptyList()
+ )
+ } else {
+ CommonBuiltinItem(settingId, preferenceKey!!)
+ }
} else {
- DeviceSettingConfigItemModel.AppProvidedItem(settingId)
+ AppProvidedItem(settingId)
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index d6b2862..7eae5b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -22,7 +22,9 @@
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
-import com.android.internal.util.ConcurrentUtils
+import android.os.IInterface
+import android.text.TextUtils
+import android.util.Log
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceInfo
@@ -34,27 +36,28 @@
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
+import com.android.settingslib.bluetooth.devicesettings.data.model.ServiceConnectionStatus
import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.atomic.AtomicReference
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -82,111 +85,164 @@
}
setAction(intentAction)
}
+
+ fun isValid(): Boolean {
+ return !TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(intentAction)
+ }
}
- private var config = AtomicReference<DeviceSettingsConfig?>(null)
- private var idToSetting = AtomicReference<Flow<Map<Int, DeviceSetting>>?>(null)
-
- /** Gets [DeviceSettingsConfig] for the device, return null when failed. */
- suspend fun getDeviceSettingsConfig(): DeviceSettingsConfig? =
- config.computeIfAbsent {
- getConfigServiceBindingIntent(cachedDevice)
- .flatMapLatest { getService(it) }
- .map { it?.let { IDeviceSettingsConfigProviderService.Stub.asInterface(it) } }
- .map {
- it?.getDeviceSettingsConfig(
- deviceInfo { setBluetoothAddress(cachedDevice.address) }
- )
+ private var isServiceEnabled =
+ coroutineScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
+ val states = getSettingsProviderServices()?.values ?: return@async false
+ combine(states) { it.toList() }
+ .mapNotNull { allStatus ->
+ if (allStatus.any { it is ServiceConnectionStatus.Failed }) {
+ false
+ } else if (allStatus.all { it is ServiceConnectionStatus.Connected }) {
+ allStatus
+ .filterIsInstance<
+ ServiceConnectionStatus.Connected<
+ IDeviceSettingsProviderService>
+ >()
+ .all { it.service.serviceStatus?.enabled == true }
+ } else {
+ null
+ }
}
.first()
}
+ private var config =
+ coroutineScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
+ val intent =
+ tryGetEndpointFromMetadata(cachedDevice)?.toIntent()
+ ?: run {
+ Log.i(TAG, "Unable to read device setting metadata from $cachedDevice")
+ return@async null
+ }
+ getService(intent, IDeviceSettingsConfigProviderService.Stub::asInterface)
+ .flatMapConcat {
+ when (it) {
+ is ServiceConnectionStatus.Connected ->
+ flowOf(
+ it.service.getDeviceSettingsConfig(
+ deviceInfo { setBluetoothAddress(cachedDevice.address) }
+ )
+ )
+ ServiceConnectionStatus.Connecting -> flowOf()
+ ServiceConnectionStatus.Failed -> flowOf(null)
+ }
+ }
+ .first()
+ }
+
+ private val settingIdToItemMapping =
+ flow {
+ if (!isServiceEnabled.await()) {
+ Log.w(TAG, "Service is disabled")
+ return@flow
+ }
+ getSettingsProviderServices()
+ ?.values
+ ?.map {
+ it.flatMapLatest { status ->
+ when (status) {
+ is ServiceConnectionStatus.Connected ->
+ getDeviceSettingsFromService(cachedDevice, status.service)
+ else -> flowOf(emptyList())
+ }
+ }
+ }
+ ?.let { items -> combine(items) { it.toList().flatten() } }
+ ?.map { items -> items.associateBy { it.settingId } }
+ ?.let { emitAll(it) }
+ }
+ .shareIn(scope = coroutineScope, started = SharingStarted.WhileSubscribed(), replay = 1)
+
+ /** Gets [DeviceSettingsConfig] for the device, return null when failed. */
+ suspend fun getDeviceSettingsConfig(): DeviceSettingsConfig? {
+ if (!isServiceEnabled.await()) {
+ Log.w(TAG, "Service is disabled")
+ return null
+ }
+ return readConfig()
+ }
+
/** Gets all device settings for the device. */
fun getDeviceSettingList(): Flow<List<DeviceSetting>> =
- getSettingIdToItemMapping().map { it.values.toList() }
+ settingIdToItemMapping.map { it.values.toList() }
/** Gets the device settings with the ID for the device. */
fun getDeviceSetting(@DeviceSettingId deviceSettingId: Int): Flow<DeviceSetting?> =
- getSettingIdToItemMapping().map { it[deviceSettingId] }
+ settingIdToItemMapping.map { it[deviceSettingId] }
/** Updates the device setting state for the device. */
suspend fun updateDeviceSettings(
@DeviceSettingId deviceSettingId: Int,
deviceSettingPreferenceState: DeviceSettingPreferenceState,
) {
- getDeviceSettingsConfig()?.let { config ->
+ if (!isServiceEnabled.await()) {
+ Log.w(TAG, "Service is disabled")
+ return
+ }
+ readConfig()?.let { config ->
(config.mainContentItems + config.moreSettingsItems)
.find { it.settingId == deviceSettingId }
?.let {
getSettingsProviderServices()
?.get(EndPoint(it.packageName, it.className, it.intentAction))
- ?.filterNotNull()
+ ?.filterIsInstance<
+ ServiceConnectionStatus.Connected<IDeviceSettingsProviderService>
+ >()
?.first()
}
+ ?.service
?.updateDeviceSettings(
deviceInfo { setBluetoothAddress(cachedDevice.address) },
DeviceSettingState.Builder()
.setSettingId(deviceSettingId)
.setPreferenceState(deviceSettingPreferenceState)
- .build()
+ .build(),
)
}
}
+ private suspend fun readConfig(): DeviceSettingsConfig? = config.await()
+
private suspend fun getSettingsProviderServices():
- Map<EndPoint, StateFlow<IDeviceSettingsProviderService?>>? =
- getDeviceSettingsConfig()
+ Map<EndPoint, StateFlow<ServiceConnectionStatus<IDeviceSettingsProviderService>>>? =
+ readConfig()
?.let { config ->
(config.mainContentItems + config.moreSettingsItems).map {
EndPoint(
packageName = it.packageName,
className = it.className,
- intentAction = it.intentAction
+ intentAction = it.intentAction,
)
}
}
+ ?.filter { it.isValid() }
?.distinct()
?.associateBy(
{ it },
{ endpoint ->
services.computeIfAbsent(endpoint) {
- getService(endpoint.toIntent())
- .map { service ->
- IDeviceSettingsProviderService.Stub.asInterface(service)
- }
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
+ getService(
+ endpoint.toIntent(),
+ IDeviceSettingsProviderService.Stub::asInterface,
+ )
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(),
+ ServiceConnectionStatus.Connecting,
+ )
}
- }
+ },
)
- private fun getSettingIdToItemMapping(): Flow<Map<Int, DeviceSetting>> =
- idToSetting.computeIfAbsent {
- flow {
- getSettingsProviderServices()
- ?.values
- ?.map {
- it.flatMapLatest { service ->
- if (service != null) {
- getDeviceSettingsFromService(cachedDevice, service)
- } else {
- flowOf(emptyList())
- }
- }
- }
- ?.let { items -> combine(items) { it.toList().flatten() } }
- ?.map { items -> items.associateBy { it.settingId } }
- ?.let { emitAll(it) }
- }
- .shareIn(
- scope = coroutineScope,
- started = SharingStarted.WhileSubscribed(),
- replay = 1
- )
- }!!
-
private fun getDeviceSettingsFromService(
cachedDevice: CachedBluetoothDevice,
- service: IDeviceSettingsProviderService
+ service: IDeviceSettingsProviderService,
): Flow<List<DeviceSetting>> {
return callbackFlow {
val listener =
@@ -202,51 +258,28 @@
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
}
- private fun getService(intent: Intent): Flow<IBinder?> {
+ private fun <T : IInterface> getService(
+ intent: Intent,
+ transform: ((IBinder) -> T),
+ ): Flow<ServiceConnectionStatus<T>> {
return callbackFlow {
val serviceConnection =
object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
- launch { send(service) }
+ launch { send(ServiceConnectionStatus.Connected(transform(service))) }
}
override fun onServiceDisconnected(name: ComponentName?) {
- launch { send(null) }
+ launch { send(ServiceConnectionStatus.Connecting) }
}
}
if (!context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
- launch { send(null) }
+ launch { send(ServiceConnectionStatus.Failed) }
}
awaitClose { context.unbindService(serviceConnection) }
}
}
- private fun getConfigServiceBindingIntent(cachedDevice: CachedBluetoothDevice): Flow<Intent> {
- return callbackFlow {
- val listener =
- BluetoothAdapter.OnMetadataChangedListener { device, key, _ ->
- if (
- key == METADATA_FAST_PAIR_CUSTOMIZED_FIELDS &&
- cachedDevice.device == device
- ) {
- launch { tryGetEndpointFromMetadata(cachedDevice)?.let { send(it) } }
- }
- }
- bluetoothAdaptor.addOnMetadataChangedListener(
- cachedDevice.device,
- ConcurrentUtils.DIRECT_EXECUTOR,
- listener,
- )
- awaitClose {
- bluetoothAdaptor.removeOnMetadataChangedListener(cachedDevice.device, listener)
- }
- }
- .onStart { tryGetEndpointFromMetadata(cachedDevice)?.let { emit(it) } }
- .distinctUntilChanged()
- .map { it.toIntent() }
- .flowOn(backgroundCoroutineContext)
- }
-
private suspend fun tryGetEndpointFromMetadata(cachedDevice: CachedBluetoothDevice): EndPoint? =
withContext(backgroundCoroutineContext) {
val packageName =
@@ -257,29 +290,31 @@
val className =
BluetoothUtils.getFastPairCustomizedField(
cachedDevice.device,
- CONFIG_SERVICE_CLASS_NAME
+ CONFIG_SERVICE_CLASS_NAME,
) ?: return@withContext null
val intentAction =
BluetoothUtils.getFastPairCustomizedField(
cachedDevice.device,
- CONFIG_SERVICE_INTENT_ACTION
+ CONFIG_SERVICE_INTENT_ACTION,
) ?: return@withContext null
EndPoint(packageName, className, intentAction)
}
- private inline fun <T> AtomicReference<T?>.computeIfAbsent(producer: () -> T): T? =
- get() ?: producer().let { compareAndExchange(null, it) ?: it }
-
private inline fun deviceInfo(block: DeviceInfo.Builder.() -> Unit): DeviceInfo {
return DeviceInfo.Builder().apply { block() }.build()
}
companion object {
+ const val TAG = "DeviceSettingSrvConn"
const val METADATA_FAST_PAIR_CUSTOMIZED_FIELDS: Int = 25
const val CONFIG_SERVICE_PACKAGE_NAME = "DEVICE_SETTINGS_CONFIG_PACKAGE_NAME"
const val CONFIG_SERVICE_CLASS_NAME = "DEVICE_SETTINGS_CONFIG_CLASS"
const val CONFIG_SERVICE_INTENT_ACTION = "DEVICE_SETTINGS_CONFIG_ACTION"
- val services = ConcurrentHashMap<EndPoint, StateFlow<IDeviceSettingsProviderService?>>()
+ val services =
+ ConcurrentHashMap<
+ EndPoint,
+ StateFlow<ServiceConnectionStatus<IDeviceSettingsProviderService>>,
+ >()
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
index c1ac763..08fb3fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
@@ -36,10 +36,23 @@
@DeviceSettingId val settingId: Int
/** A built-in item in Settings. */
- data class BuiltinItem(
- @DeviceSettingId override val settingId: Int,
- val preferenceKey: String?
- ) : DeviceSettingConfigItemModel
+ sealed interface BuiltinItem : DeviceSettingConfigItemModel {
+ @DeviceSettingId override val settingId: Int
+ val preferenceKey: String
+
+ /** A general built-in item in Settings. */
+ data class CommonBuiltinItem(
+ @DeviceSettingId override val settingId: Int,
+ override val preferenceKey: String,
+ ) : BuiltinItem
+
+ /** A bluetooth profiles in Settings. */
+ data class BluetoothProfilesItem(
+ @DeviceSettingId override val settingId: Int,
+ override val preferenceKey: String,
+ val invisibleProfiles: List<String>,
+ ) : BuiltinItem
+ }
/** A remote item provided by other apps. */
data class AppProvidedItem(@DeviceSettingId override val settingId: Int) :
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
index bd1e5a5..7994924 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
@@ -31,13 +31,14 @@
import android.view.MenuItem;
import androidx.lifecycle.LifecycleOwner;
-import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
+import com.android.settingslib.preference.PreferenceFragment;
+
/**
- * {@link PreferenceFragmentCompat} that has hooks to observe fragment lifecycle events.
+ * Preference fragment that has hooks to observe fragment lifecycle events.
*/
-public abstract class ObservablePreferenceFragment extends PreferenceFragmentCompat
+public abstract class ObservablePreferenceFragment extends PreferenceFragment
implements LifecycleOwner {
private final Lifecycle mLifecycle = new Lifecycle(this);
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
index 4371f05..c686708 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
@@ -41,6 +41,8 @@
override val modes: Flow<List<ZenMode>>
get() = mutableModesFlow.asStateFlow()
+ override fun getModes(): List<ZenMode> = mutableModesFlow.value
+
private val activeModesDurations = mutableMapOf<String, Duration?>()
init {
@@ -59,6 +61,10 @@
mutableModesFlow.value += zenModes
}
+ fun addMode(mode: ZenMode) {
+ mutableModesFlow.value += mode
+ }
+
fun addMode(id: String, @AutomaticZenRule.Type type: Int = AutomaticZenRule.TYPE_UNKNOWN,
active: Boolean = false) {
mutableModesFlow.value += newMode(id, type, active)
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
index 0ff7f84..7fdbcda 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
@@ -59,6 +59,8 @@
/** A list of all existing priority modes. */
val modes: Flow<List<ZenMode>>
+ fun getModes(): List<ZenMode>
+
fun activateMode(zenMode: ZenMode, duration: Duration? = null)
fun deactivateMode(zenMode: ZenMode)
@@ -184,6 +186,15 @@
}
}
+ /**
+ * Gets the current list of [ZenMode] instances according to the backend.
+ *
+ * This is necessary, and cannot be supplanted by making [modes] a StateFlow, because it will be
+ * called whenever we know or suspect that [modes] may not have caught up to the latest data
+ * (such as right after a user switch).
+ */
+ override fun getModes(): List<ZenMode> = backend.modes
+
override fun activateMode(zenMode: ZenMode, duration: Duration?) {
backend.activateMode(zenMode, duration)
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
index f7492cf..712ddc8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
@@ -16,6 +16,7 @@
package com.android.settingslib.notification.modes;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
@@ -24,11 +25,13 @@
import android.content.ComponentName;
import android.net.Uri;
import android.service.notification.Condition;
+import android.service.notification.SystemZenRules;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Random;
@@ -40,13 +43,27 @@
private ZenModeConfig.ZenRule mConfigZenRule;
public static final ZenMode EXAMPLE = new TestModeBuilder().build();
- public static final ZenMode MANUAL_DND_ACTIVE = manualDnd(Uri.EMPTY, true);
- public static final ZenMode MANUAL_DND_INACTIVE = manualDnd(Uri.EMPTY, false);
- public static ZenMode manualDnd(Uri conditionId, boolean isActive) {
+ public static final ZenMode MANUAL_DND_ACTIVE = manualDnd(Uri.EMPTY,
+ INTERRUPTION_FILTER_PRIORITY, true);
+
+ public static final ZenMode MANUAL_DND_INACTIVE = manualDnd(Uri.EMPTY,
+ INTERRUPTION_FILTER_PRIORITY, false);
+
+ @NonNull
+ public static ZenMode manualDnd(@NotificationManager.InterruptionFilter int filter,
+ boolean isActive) {
+ return manualDnd(Uri.EMPTY, filter, isActive);
+ }
+
+ private static ZenMode manualDnd(Uri conditionId,
+ @NotificationManager.InterruptionFilter int filter, boolean isActive) {
return ZenMode.manualDndMode(
new AutomaticZenRule.Builder("Do Not Disturb", conditionId)
- .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
+ .setInterruptionFilter(filter)
+ .setType(AutomaticZenRule.TYPE_OTHER)
+ .setManualInvocationAllowed(true)
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
.setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
.build(),
isActive);
@@ -58,7 +75,7 @@
mId = "rule_" + id;
mRule = new AutomaticZenRule.Builder("Test Rule #" + id, Uri.parse("rule://" + id))
.setPackage("some_package")
- .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
.build();
mConfigZenRule = new ZenModeConfig.ZenRule();
@@ -68,7 +85,7 @@
public TestModeBuilder(ZenMode previous) {
mId = previous.getId();
- mRule = previous.getRule();
+ mRule = new AutomaticZenRule.Builder(previous.getRule()).build();
mConfigZenRule = new ZenModeConfig.ZenRule();
mConfigZenRule.enabled = previous.getRule().isEnabled();
@@ -134,7 +151,7 @@
@NotificationManager.InterruptionFilter int interruptionFilter) {
mRule.setInterruptionFilter(interruptionFilter);
mConfigZenRule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
- interruptionFilter, NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ interruptionFilter, INTERRUPTION_FILTER_PRIORITY);
return this;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
index 0a0b65b..79dabf0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
@@ -37,7 +37,7 @@
* chosen one via Settings).
*/
static final ZenIcon.Key IMPLICIT_MODE_DEFAULT = ZenIcon.Key.forSystemResource(
- R.drawable.ic_zen_mode_type_unknown);
+ R.drawable.ic_zen_mode_type_special_dnd);
private static final ImmutableMap<Integer, ZenIcon.Key> TYPE_DEFAULTS = ImmutableMap.of(
AutomaticZenRule.TYPE_UNKNOWN,
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
index fe0f98a..43c6c50 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
@@ -31,7 +31,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.ListenableFuture;
@@ -53,25 +52,22 @@
private final LruCache<ZenIcon.Key, Drawable> mCache;
private final ListeningExecutorService mBackgroundExecutor;
+ /** Obtains the singleton {@link ZenIconLoader}. */
public static ZenIconLoader getInstance() {
if (sInstance == null) {
- sInstance = new ZenIconLoader();
+ sInstance = new ZenIconLoader(Executors.newFixedThreadPool(4));
}
return sInstance;
}
- /** Replaces the singleton instance of {@link ZenIconLoader} by the provided one. */
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- public static void setInstance(@Nullable ZenIconLoader instance) {
- sInstance = instance;
- }
-
- private ZenIconLoader() {
- this(Executors.newFixedThreadPool(4));
- }
-
- @VisibleForTesting
- public ZenIconLoader(ExecutorService backgroundExecutor) {
+ /**
+ * Constructs a ZenIconLoader with the specified {@code backgroundExecutor}.
+ *
+ * <p>ZenIconLoader <em>should be a singleton</em>, so this should only be used to instantiate
+ * and provide the singleton instance in a module. If the app doesn't support dependency
+ * injection, use {@link #getInstance} instead.
+ */
+ public ZenIconLoader(@NonNull ExecutorService backgroundExecutor) {
mCache = new LruCache<>(50);
mBackgroundExecutor =
MoreExecutors.listeningDecorator(backgroundExecutor);
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
index 36975c7..7b2a284 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
@@ -18,9 +18,10 @@
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleTime;
import static android.service.notification.ZenModeConfig.tryParseCountdownConditionId;
@@ -68,23 +69,6 @@
static final String MANUAL_DND_MODE_ID = ZenModeConfig.MANUAL_RULE_ID;
static final String TEMP_NEW_MODE_ID = "temp_new_mode";
- // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
- private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
- new ZenPolicy.Builder()
- .disallowAllSounds()
- .allowAlarms(true)
- .allowMedia(true)
- .allowPriorityChannels(false)
- .build();
-
- // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
- private static final ZenPolicy POLICY_INTERRUPTION_FILTER_NONE =
- new ZenPolicy.Builder()
- .disallowAllSounds()
- .hideAllVisualEffects()
- .allowPriorityChannels(false)
- .build();
-
private static final Comparator<Integer> PRIORITIZED_TYPE_COMPARATOR = new Comparator<>() {
private static final ImmutableList</* @AutomaticZenRule.Type */ Integer>
@@ -171,13 +155,9 @@
}
static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
- // Manual rule is owned by the system, so we set it here
- AutomaticZenRule manualRuleWithPkg = new AutomaticZenRule.Builder(manualRule)
- .setPackage(PACKAGE_ANDROID)
- .build();
return new ZenMode(
MANUAL_DND_MODE_ID,
- manualRuleWithPkg,
+ manualRule,
Kind.MANUAL_DND,
isActive ? Status.ENABLED_AND_ACTIVE : Status.ENABLED);
}
@@ -298,6 +278,23 @@
}
}
+ /** Returns the interruption filter of the mode. */
+ @NotificationManager.InterruptionFilter
+ public int getInterruptionFilter() {
+ return mRule.getInterruptionFilter();
+ }
+
+ /**
+ * Sets the interruption filter of the mode. This is valid for {@link AutomaticZenRule}-backed
+ * modes (and not manual DND).
+ */
+ public void setInterruptionFilter(@NotificationManager.InterruptionFilter int filter) {
+ if (isManualDnd() || !canEditPolicy()) {
+ throw new IllegalStateException("Cannot update interruption filter for mode " + this);
+ }
+ mRule.setInterruptionFilter(filter);
+ }
+
@NonNull
public ZenPolicy getPolicy() {
switch (mRule.getInterruptionFilter()) {
@@ -306,10 +303,12 @@
return requireNonNull(mRule.getZenPolicy());
case NotificationManager.INTERRUPTION_FILTER_ALARMS:
- return POLICY_INTERRUPTION_FILTER_ALARMS;
+ return new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy()).build()
+ .overwrittenWith(ZenPolicy.getBasePolicyInterruptionFilterAlarms());
case NotificationManager.INTERRUPTION_FILTER_NONE:
- return POLICY_INTERRUPTION_FILTER_NONE;
+ return new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy()).build()
+ .overwrittenWith(ZenPolicy.getBasePolicyInterruptionFilterNone());
case NotificationManager.INTERRUPTION_FILTER_UNKNOWN:
default:
@@ -326,6 +325,10 @@
*/
@SuppressLint("WrongConstant")
public void setPolicy(@NonNull ZenPolicy policy) {
+ if (!canEditPolicy()) {
+ throw new IllegalStateException("Cannot update ZenPolicy for mode " + this);
+ }
+
ZenPolicy currentPolicy = getPolicy();
if (currentPolicy.equals(policy)) {
return;
@@ -342,6 +345,12 @@
mRule.setZenPolicy(policy);
}
+ /**
+ * Returns the {@link ZenDeviceEffects} of the mode.
+ *
+ * <p>This is never {@code null}; if the backing AutomaticZenRule doesn't have effects set then
+ * a default (empty) effects set is returned.
+ */
@NonNull
public ZenDeviceEffects getDeviceEffects() {
return mRule.getDeviceEffects() != null
@@ -349,6 +358,15 @@
: new ZenDeviceEffects.Builder().build();
}
+ /** Sets the {@link ZenDeviceEffects} of the mode. */
+ public void setDeviceEffects(@NonNull ZenDeviceEffects effects) {
+ checkNotNull(effects);
+ if (!canEditPolicy()) {
+ throw new IllegalStateException("Cannot update device effects for mode " + this);
+ }
+ mRule.setDeviceEffects(effects);
+ }
+
public void setCustomModeConditionId(Context context, Uri conditionId) {
checkState(SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName()),
"Trying to change condition of non-system-owned rule %s (to %s)",
@@ -391,6 +409,18 @@
return !isManualDnd();
}
+ /**
+ * Whether the mode has an editable policy. Calling {@link #setPolicy},
+ * {@link #setDeviceEffects}, or {@link #setInterruptionFilter} is not valid for modes with a
+ * read-only policy.
+ */
+ public boolean canEditPolicy() {
+ // Cannot edit the policy of a temporarily active non-PRIORITY DND mode.
+ // Note that it's fine to edit the policy of an *AutomaticZenRule* with non-PRIORITY filter;
+ // the filter will we set to PRIORITY if you do.
+ return !isManualDndWithSpecialFilter();
+ }
+
public boolean canBeDeleted() {
return !isManualDnd();
}
@@ -399,6 +429,12 @@
return mKind == Kind.MANUAL_DND;
}
+ private boolean isManualDndWithSpecialFilter() {
+ return isManualDnd()
+ && (mRule.getInterruptionFilter() == INTERRUPTION_FILTER_ALARMS
+ || mRule.getInterruptionFilter() == INTERRUPTION_FILTER_NONE);
+ }
+
/**
* A <em>custom manual</em> mode is a mode created by the user, and not yet assigned an
* automatic trigger condition (neither time schedule nor a calendar).
@@ -414,6 +450,18 @@
return mRule.isEnabled();
}
+ /**
+ * Enables or disables the mode.
+ *
+ * <p>The DND mode cannot be disabled; trying to do so will fail.
+ */
+ public void setEnabled(boolean enabled) {
+ if (isManualDnd()) {
+ throw new IllegalStateException("Cannot update enabled for manual DND mode " + this);
+ }
+ mRule.setEnabled(enabled);
+ }
+
public boolean isActive() {
return mStatus == Status.ENABLED_AND_ACTIVE;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
index c8a12f4..71e03c1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
@@ -16,6 +16,11 @@
package com.android.settingslib.notification.modes;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.app.NotificationManager.zenModeToInterruptionFilter;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AutomaticZenRule;
@@ -112,14 +117,22 @@
private ZenMode getManualDndMode(ZenModeConfig config) {
ZenModeConfig.ZenRule manualRule = config.manualRule;
+
+ // If DND is currently on with an interruption filter other than PRIORITY, construct the
+ // rule with that. DND will be *non-editable* while in this state.
+ int dndInterruptionFilter = config.isManualActive()
+ ? zenModeToInterruptionFilter(manualRule.zenMode)
+ : INTERRUPTION_FILTER_PRIORITY;
+
AutomaticZenRule manualDndRule = new AutomaticZenRule.Builder(
- mContext.getString(R.string.zen_mode_settings_title), manualRule.conditionId)
- .setType(manualRule.type)
+ mContext.getString(R.string.zen_mode_do_not_disturb_name), manualRule.conditionId)
+ .setPackage(PACKAGE_ANDROID)
+ .setType(AutomaticZenRule.TYPE_OTHER)
.setZenPolicy(manualRule.zenPolicy)
.setDeviceEffects(manualRule.zenDeviceEffects)
- .setManualInvocationAllowed(manualRule.allowManualInvocation)
+ .setManualInvocationAllowed(true)
.setConfigurationActivity(null) // No further settings
- .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
+ .setInterruptionFilter(dndInterruptionFilter)
.build();
return ZenMode.manualDndMode(manualDndRule, config.isManualActive());
@@ -150,7 +163,7 @@
durationConditionId = ZenModeConfig.toTimeCondition(mContext,
(int) forDuration.toMinutes(), ActivityManager.getCurrentUser(), true).id;
}
- mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ mNotificationManager.setZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
durationConditionId, TAG, /* fromUser= */ true);
} else {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index cdc3f12..f38e91a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -32,6 +32,7 @@
import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.R;
import com.android.settingslib.utils.ThreadUtils;
import com.google.common.util.concurrent.FutureCallback;
@@ -132,6 +133,13 @@
intent.addCategory(Intent.CATEGORY_DEFAULT);
if (Flags.avatarSync()) {
intent.putExtra(EXTRA_IS_USER_NEW, isUserNew);
+ // Fix vulnerability b/341688848 by explicitly set the class name of avatar picker.
+ if (Flags.fixAvatarCrossUserLeak()) {
+ final String packageName =
+ mActivity.getString(R.string.config_avatar_picker_package);
+ final String className = mActivity.getString(R.string.config_avatar_picker_class);
+ intent.setClassName(packageName, className);
+ }
} else {
// SettingsLib is used by multiple apps therefore we need to know out of all apps
// using settingsLib which one is the one we return value to.
diff --git a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt b/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
deleted file mode 100644
index 0b71d25..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
+++ /dev/null
@@ -1,113 +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.data.repository
-
-import android.view.accessibility.CaptioningManager
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.ProducerScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-interface CaptioningRepository {
-
- /** The system audio caption enabled state. */
- val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
-
- /** The system audio caption UI enabled state. */
- val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
-
- /** Sets [isSystemAudioCaptioningEnabled]. */
- suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean)
-}
-
-class CaptioningRepositoryImpl(
- private val captioningManager: CaptioningManager,
- private val backgroundCoroutineContext: CoroutineContext,
- coroutineScope: CoroutineScope,
-) : CaptioningRepository {
-
- private val captioningChanges: SharedFlow<CaptioningChange> =
- callbackFlow {
- val listener = CaptioningChangeProducingListener(this)
- captioningManager.addCaptioningChangeListener(listener)
- awaitClose { captioningManager.removeCaptioningChangeListener(listener) }
- }
- .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0)
-
- override val isSystemAudioCaptioningEnabled: StateFlow<Boolean> =
- captioningChanges
- .filterIsInstance(CaptioningChange.IsSystemAudioCaptioningEnabled::class)
- .map { it.isEnabled }
- .onStart { emit(captioningManager.isSystemAudioCaptioningEnabled) }
- .stateIn(
- coroutineScope,
- SharingStarted.WhileSubscribed(),
- captioningManager.isSystemAudioCaptioningEnabled,
- )
-
- override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> =
- captioningChanges
- .filterIsInstance(CaptioningChange.IsSystemUICaptioningEnabled::class)
- .map { it.isEnabled }
- .onStart { emit(captioningManager.isSystemAudioCaptioningUiEnabled) }
- .stateIn(
- coroutineScope,
- SharingStarted.WhileSubscribed(),
- captioningManager.isSystemAudioCaptioningUiEnabled,
- )
-
- override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) {
- withContext(backgroundCoroutineContext) {
- captioningManager.isSystemAudioCaptioningEnabled = isEnabled
- }
- }
-
- private sealed interface CaptioningChange {
-
- data class IsSystemAudioCaptioningEnabled(val isEnabled: Boolean) : CaptioningChange
-
- data class IsSystemUICaptioningEnabled(val isEnabled: Boolean) : CaptioningChange
- }
-
- private class CaptioningChangeProducingListener(
- private val scope: ProducerScope<CaptioningChange>
- ) : CaptioningManager.CaptioningChangeListener() {
-
- override fun onSystemAudioCaptioningChanged(enabled: Boolean) {
- emitChange(CaptioningChange.IsSystemAudioCaptioningEnabled(enabled))
- }
-
- override fun onSystemAudioCaptioningUiChanged(enabled: Boolean) {
- emitChange(CaptioningChange.IsSystemUICaptioningEnabled(enabled))
- }
-
- private fun emitChange(change: CaptioningChange) {
- scope.launch { scope.send(change) }
- }
- }
-}
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/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 3e2d832..d3c345d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -98,7 +98,7 @@
*/
suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean
- suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode)
+ suspend fun setRingerModeInternal(audioStream: AudioStream, mode: RingerMode)
/** Gets audio device category. */
@AudioDeviceCategory suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int
@@ -248,8 +248,8 @@
}
}
- override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) {
- withContext(backgroundCoroutineContext) { audioManager.ringerMode = mode.value }
+ override suspend fun setRingerModeInternal(audioStream: AudioStream, mode: RingerMode) {
+ withContext(backgroundCoroutineContext) { audioManager.ringerModeInternal = mode.value }
}
@AudioDeviceCategory
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
index 08863b5..dca890d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
@@ -68,7 +68,7 @@
if (audioStream.value == AudioManager.STREAM_RING) {
val mode =
if (isMuted) AudioManager.RINGER_MODE_VIBRATE else AudioManager.RINGER_MODE_NORMAL
- audioRepository.setRingerMode(audioStream, RingerMode(mode))
+ audioRepository.setRingerModeInternal(audioStream, RingerMode(mode))
}
val mutedChanged = audioRepository.setMuted(audioStream, isMuted)
if (mutedChanged && !isMuted) {
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 15fe8ed..03dd712 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -72,3 +72,10 @@
dxflags: ["--multi-dex"],
manifest: "AndroidManifest.xml",
}
+
+test_module_config {
+ name: "SettingsLibTests_settingslib_users",
+ base: "SettingsLibTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.settingslib.users."],
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 52e6391..8a3b1dfb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -74,6 +74,8 @@
private lateinit var underTest: AudioRepository
+ private var ringerModeInternal: RingerMode = RingerMode(AudioManager.RINGER_MODE_NORMAL)
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -82,7 +84,7 @@
`when`(audioManager.communicationDevice).thenReturn(communicationDevice)
`when`(audioManager.getStreamMinVolume(anyInt())).thenReturn(MIN_VOLUME)
`when`(audioManager.getStreamMaxVolume(anyInt())).thenReturn(MAX_VOLUME)
- `when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+ `when`(audioManager.ringerModeInternal).then { ringerModeInternal.value }
`when`(audioManager.setStreamVolume(anyInt(), anyInt(), anyInt())).then {
val streamType = it.arguments[0] as Int
volumeByStream[it.arguments[0] as Int] = it.arguments[1] as Int
@@ -103,6 +105,10 @@
`when`(audioManager.isStreamMute(anyInt())).thenAnswer {
isMuteByStream.getOrDefault(it.arguments[0] as Int, false)
}
+ `when`(audioManager.setRingerModeInternal(anyInt())).then {
+ ringerModeInternal = RingerMode(it.arguments[0] as Int)
+ Unit
+ }
underTest =
AudioRepositoryImpl(
@@ -137,7 +143,7 @@
underTest.ringerMode.onEach { modes.add(it) }.launchIn(backgroundScope)
runCurrent()
- `when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
+ ringerModeInternal = RingerMode(AudioManager.RINGER_MODE_SILENT)
triggerEvent(AudioManagerEvent.InternalRingerModeChanged)
runCurrent()
@@ -150,6 +156,19 @@
}
@Test
+ fun changingRingerMode_changesRingerModeInternal() {
+ testScope.runTest {
+ underTest.setRingerModeInternal(
+ AudioStream(AudioManager.STREAM_SYSTEM),
+ RingerMode(AudioManager.RINGER_MODE_SILENT),
+ )
+ runCurrent()
+
+ assertThat(ringerModeInternal).isEqualTo(RingerMode(AudioManager.RINGER_MODE_SILENT))
+ }
+ }
+
+ @Test
fun communicationDeviceChanges_repositoryEmits() {
testScope.runTest {
var device: AudioDeviceInfo? = null
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/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.kt
new file mode 100644
index 0000000..aa22fac
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.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.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class DeviceSettingsProviderServiceStatusTest {
+
+ @Test
+ fun parcelOperation() {
+ val item =
+ DeviceSettingsProviderServiceStatus(
+ enabled = true,
+ extras = Bundle().apply { putString("key1", "value1") },
+ )
+
+ val fromParcel = writeAndRead(item)
+
+ assertThat(fromParcel.enabled).isEqualTo(item.enabled)
+ assertThat(fromParcel.extras.getString("key1")).isEqualTo(item.extras.getString("key1"))
+ }
+
+ private fun writeAndRead(
+ item: DeviceSettingsProviderServiceStatus
+ ): DeviceSettingsProviderServiceStatus {
+ val parcel = Parcel.obtain()
+ item.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ return DeviceSettingsProviderServiceStatus.CREATOR.createFromParcel(parcel)
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index 95ee46e..81b5634 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -33,6 +33,7 @@
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsProviderServiceStatus
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
@@ -47,10 +48,8 @@
import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -59,12 +58,9 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.verify
@@ -85,9 +81,6 @@
@Mock private lateinit var configService: IDeviceSettingsConfigProviderService.Stub
@Mock private lateinit var settingProviderService1: IDeviceSettingsProviderService.Stub
@Mock private lateinit var settingProviderService2: IDeviceSettingsProviderService.Stub
- @Captor
- private lateinit var metadataChangeCaptor:
- ArgumentCaptor<BluetoothAdapter.OnMetadataChangedListener>
private lateinit var underTest: DeviceSettingRepository
private val testScope = TestScope()
@@ -98,7 +91,9 @@
`when`(cachedDevice.address).thenReturn(BLUETOOTH_ADDRESS)
`when`(
bluetoothDevice.getMetadata(
- DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+ DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
+ )
+ )
.thenReturn(BLUETOOTH_DEVICE_METADATA.toByteArray())
`when`(configService.queryLocalInterface(anyString())).thenReturn(configService)
@@ -121,7 +116,8 @@
connection.onServiceConnected(
ComponentName(
SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
- SETTING_PROVIDER_SERVICE_CLASS_NAME_1),
+ SETTING_PROVIDER_SERVICE_CLASS_NAME_1,
+ ),
settingProviderService1,
)
SETTING_PROVIDER_SERVICE_INTENT_ACTION_2 ->
@@ -153,40 +149,60 @@
fun getDeviceSettingsConfig_withMetadata_success() {
testScope.runTest {
`when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
val config = underTest.getDeviceSettingsConfig(cachedDevice)
assertConfig(config!!, DEVICE_SETTING_CONFIG)
+ assertThat(config.mainItems[0])
+ .isInstanceOf(DeviceSettingConfigItemModel.AppProvidedItem::class.java)
+ assertThat(config.mainItems[1])
+ .isInstanceOf(
+ DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem::class.java
+ )
+ assertThat(config.mainItems[2])
+ .isInstanceOf(
+ DeviceSettingConfigItemModel.BuiltinItem.BluetoothProfilesItem::class.java
+ )
}
}
@Test
- fun getDeviceSettingsConfig_waitMetadataChange_success() {
+ fun getDeviceSettingsConfig_noMetadata_returnNull() {
+ testScope.runTest {
+ `when`(
+ bluetoothDevice.getMetadata(
+ DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
+ )
+ )
+ .thenReturn("".toByteArray())
+ `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+
+ val config = underTest.getDeviceSettingsConfig(cachedDevice)
+
+ assertThat(config).isNull()
+ }
+ }
+
+ @Test
+ fun getDeviceSettingsConfig_providerServiceNotEnabled_returnNull() {
testScope.runTest {
`when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
- `when`(
- bluetoothDevice.getMetadata(
- DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
- .thenReturn("".toByteArray())
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(false))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
- var config: DeviceSettingConfigModel? = null
- val job = launch { config = underTest.getDeviceSettingsConfig(cachedDevice) }
- delay(1000)
- verify(bluetoothAdapter)
- .addOnMetadataChangedListener(
- eq(bluetoothDevice), any(), metadataChangeCaptor.capture())
- metadataChangeCaptor.value.onMetadataChanged(
- bluetoothDevice,
- DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
- BLUETOOTH_DEVICE_METADATA.toByteArray(),
- )
- `when`(
- bluetoothDevice.getMetadata(
- DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
- .thenReturn(BLUETOOTH_DEVICE_METADATA.toByteArray())
+ val config = underTest.getDeviceSettingsConfig(cachedDevice)
- job.join()
- assertConfig(config!!, DEVICE_SETTING_CONFIG)
+ assertThat(config).isNull()
}
}
@@ -212,6 +228,10 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
}
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
var setting: DeviceSettingModel? = null
underTest
@@ -234,6 +254,10 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
}
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
var setting: DeviceSettingModel? = null
underTest
@@ -251,11 +275,15 @@
testScope.runTest {
`when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
`when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
- input ->
+ input ->
input
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_HELP))
}
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
var setting: DeviceSettingModel? = null
underTest
@@ -299,6 +327,10 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
}
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
var setting: DeviceSettingModel? = null
underTest
@@ -316,8 +348,10 @@
DeviceSettingState.Builder()
.setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
.setPreferenceState(
- ActionSwitchPreferenceState.Builder().setChecked(false).build())
- .build())
+ ActionSwitchPreferenceState.Builder().setChecked(false).build()
+ )
+ .build(),
+ )
}
}
@@ -331,6 +365,10 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
}
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
var setting: DeviceSettingModel? = null
underTest
@@ -348,8 +386,10 @@
DeviceSettingState.Builder()
.setSettingId(DeviceSettingId.DEVICE_SETTING_ID_ANC)
.setPreferenceState(
- MultiTogglePreferenceState.Builder().setState(2).build())
- .build())
+ MultiTogglePreferenceState.Builder().setState(2).build()
+ )
+ .build(),
+ )
}
}
@@ -400,7 +440,7 @@
private fun assertConfig(
actual: DeviceSettingConfigModel,
- serviceResponse: DeviceSettingsConfig
+ serviceResponse: DeviceSettingsConfig,
) {
assertThat(actual.mainItems.size).isEqualTo(serviceResponse.mainContentItems.size)
for (i in 0..<actual.mainItems.size) {
@@ -414,7 +454,7 @@
private fun assertConfigItem(
actual: DeviceSettingConfigItemModel,
- serviceResponse: DeviceSettingItem
+ serviceResponse: DeviceSettingItem,
) {
assertThat(actual.settingId).isEqualTo(serviceResponse.settingId)
}
@@ -448,24 +488,43 @@
"</DEVICE_SETTINGS_CONFIG_ACTION>"
val DEVICE_INFO = DeviceInfo.Builder().setBluetoothAddress(BLUETOOTH_ADDRESS).build()
const val DEVICE_SETTING_ID_HELP = 12345
- val DEVICE_SETTING_ITEM_1 =
+ val DEVICE_SETTING_APP_PROVIDED_ITEM_1 =
DeviceSettingItem(
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
SETTING_PROVIDER_SERVICE_CLASS_NAME_1,
- SETTING_PROVIDER_SERVICE_INTENT_ACTION_1)
- val DEVICE_SETTING_ITEM_2 =
+ SETTING_PROVIDER_SERVICE_INTENT_ACTION_1,
+ )
+ val DEVICE_SETTING_APP_PROVIDED_ITEM_2 =
DeviceSettingItem(
DeviceSettingId.DEVICE_SETTING_ID_ANC,
SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2,
SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
- SETTING_PROVIDER_SERVICE_INTENT_ACTION_2)
+ SETTING_PROVIDER_SERVICE_INTENT_ACTION_2,
+ )
+ val DEVICE_SETTING_BUILT_IN_ITEM =
+ DeviceSettingItem(
+ DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_AUDIO_DEVICE_TYPE_GROUP,
+ "",
+ "",
+ "",
+ "device_type",
+ )
+ val DEVICE_SETTING_BUILT_IN_BT_PROFILES_ITEM =
+ DeviceSettingItem(
+ DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES,
+ "",
+ "",
+ "",
+ "bluetooth_profiles",
+ )
val DEVICE_SETTING_HELP_ITEM =
DeviceSettingItem(
DEVICE_SETTING_ID_HELP,
SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2,
SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
- SETTING_PROVIDER_SERVICE_INTENT_ACTION_2)
+ SETTING_PROVIDER_SERVICE_INTENT_ACTION_2,
+ )
val DEVICE_SETTING_1 =
DeviceSetting.Builder()
.setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
@@ -474,7 +533,8 @@
.setTitle("title1")
.setHasSwitch(true)
.setAllowedChangingState(true)
- .build())
+ .build()
+ )
.build()
val DEVICE_SETTING_2 =
DeviceSetting.Builder()
@@ -487,22 +547,30 @@
ToggleInfo.Builder()
.setLabel("label1")
.setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
- .build())
+ .build()
+ )
.addToggleInfo(
ToggleInfo.Builder()
.setLabel("label2")
.setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
- .build())
- .build())
+ .build()
+ )
+ .build()
+ )
.build()
- val DEVICE_SETTING_HELP = DeviceSetting.Builder()
- .setSettingId(DEVICE_SETTING_ID_HELP)
- .setPreference(DeviceSettingHelpPreference.Builder().setIntent(Intent()).build())
- .build()
+ val DEVICE_SETTING_HELP =
+ DeviceSetting.Builder()
+ .setSettingId(DEVICE_SETTING_ID_HELP)
+ .setPreference(DeviceSettingHelpPreference.Builder().setIntent(Intent()).build())
+ .build()
val DEVICE_SETTING_CONFIG =
DeviceSettingsConfig(
- listOf(DEVICE_SETTING_ITEM_1),
- listOf(DEVICE_SETTING_ITEM_2),
+ listOf(
+ DEVICE_SETTING_APP_PROVIDED_ITEM_1,
+ DEVICE_SETTING_BUILT_IN_ITEM,
+ DEVICE_SETTING_BUILT_IN_BT_PROFILES_ITEM,
+ ),
+ listOf(DEVICE_SETTING_APP_PROVIDED_ITEM_2),
DEVICE_SETTING_HELP_ITEM,
)
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
index 67c73b1..c136644 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
@@ -199,6 +199,15 @@
}
}
+ @EnableFlags(android.app.Flags.FLAG_MODES_UI)
+ @Test
+ fun getModes_returnsModes() {
+ val modesList = listOf(TestModeBuilder().setId("One").build())
+ `when`(zenModesBackend.modes).thenReturn(modesList)
+
+ assertThat(underTest.getModes()).isEqualTo(modesList)
+ }
+
private fun triggerIntent(action: String, extras: Map<String, Parcelable>? = null) {
verify(context).registerReceiver(receiverCaptor.capture(), any(), any(), any())
val intent = Intent(action)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
index e64b0c6..d08d91d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
@@ -24,6 +24,7 @@
import static android.app.AutomaticZenRule.TYPE_THEATER;
import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
@@ -31,11 +32,14 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertThrows;
+
import android.app.AutomaticZenRule;
import android.net.Uri;
import android.os.Parcel;
import android.service.notification.Condition;
import android.service.notification.SystemZenRules;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
@@ -69,20 +73,26 @@
.build();
@Test
- public void testBasicMethods() {
+ public void testBasicMethods_mode() {
ZenMode zenMode = new ZenMode("id", ZEN_RULE, zenConfigRuleFor(ZEN_RULE, true));
assertThat(zenMode.getId()).isEqualTo("id");
assertThat(zenMode.getRule()).isEqualTo(ZEN_RULE);
assertThat(zenMode.isManualDnd()).isFalse();
assertThat(zenMode.canEditNameAndIcon()).isTrue();
+ assertThat(zenMode.canEditPolicy()).isTrue();
assertThat(zenMode.canBeDeleted()).isTrue();
assertThat(zenMode.isActive()).isTrue();
+ }
- ZenMode manualMode = ZenMode.manualDndMode(ZEN_RULE, false);
+ @Test
+ public void testBasicMethods_manualDnd() {
+ ZenMode manualMode = TestModeBuilder.MANUAL_DND_INACTIVE;
+
assertThat(manualMode.getId()).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
assertThat(manualMode.isManualDnd()).isTrue();
assertThat(manualMode.canEditNameAndIcon()).isFalse();
+ assertThat(manualMode.canEditPolicy()).isTrue();
assertThat(manualMode.canBeDeleted()).isFalse();
assertThat(manualMode.isActive()).isFalse();
assertThat(manualMode.getRule().getPackageName()).isEqualTo(PACKAGE_ANDROID);
@@ -203,7 +213,7 @@
ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));
assertThat(zenMode.getPolicy()).isEqualTo(
- new ZenPolicy.Builder()
+ new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy())
.disallowAllSounds()
.allowAlarms(true)
.allowMedia(true)
@@ -220,9 +230,8 @@
ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));
assertThat(zenMode.getPolicy()).isEqualTo(
- new ZenPolicy.Builder()
+ new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy())
.disallowAllSounds()
- .hideAllVisualEffects()
.allowPriorityChannels(false)
.build());
}
@@ -243,6 +252,77 @@
}
@Test
+ public void getInterruptionFilter_returnsFilter() {
+ ZenMode mode = new TestModeBuilder().setInterruptionFilter(
+ INTERRUPTION_FILTER_ALARMS).build();
+
+ assertThat(mode.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
+ }
+
+ @Test
+ public void setInterruptionFilter_setsFilter() {
+ ZenMode mode = new TestModeBuilder().setInterruptionFilter(
+ INTERRUPTION_FILTER_ALARMS).build();
+
+ mode.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
+
+ assertThat(mode.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
+ }
+
+ @Test
+ public void setInterruptionFilter_manualDnd_throws() {
+ ZenMode manualDnd = TestModeBuilder.MANUAL_DND_INACTIVE;
+
+ assertThrows(IllegalStateException.class,
+ () -> manualDnd.setInterruptionFilter(INTERRUPTION_FILTER_ALL));
+ }
+
+ @Test
+ public void canEditPolicy_onlyFalseForSpecialDnd() {
+ assertThat(TestModeBuilder.EXAMPLE.canEditPolicy()).isTrue();
+ assertThat(TestModeBuilder.MANUAL_DND_ACTIVE.canEditPolicy()).isTrue();
+ assertThat(TestModeBuilder.MANUAL_DND_INACTIVE.canEditPolicy()).isTrue();
+
+ ZenMode dndWithAlarms = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_ALARMS, true);
+ assertThat(dndWithAlarms.canEditPolicy()).isFalse();
+ ZenMode dndWithNone = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
+ assertThat(dndWithNone.canEditPolicy()).isFalse();
+
+ // Note: Backend will never return an inactive manual mode with custom filter.
+ ZenMode badDndWithAlarms = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_ALARMS, false);
+ assertThat(badDndWithAlarms.canEditPolicy()).isFalse();
+ ZenMode badDndWithNone = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, false);
+ assertThat(badDndWithNone.canEditPolicy()).isFalse();
+ }
+
+ @Test
+ public void canEditPolicy_whenTrue_allowsSettingPolicyAndEffects() {
+ ZenMode normalDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_PRIORITY, true);
+
+ assertThat(normalDnd.canEditPolicy()).isTrue();
+
+ ZenPolicy somePolicy = new ZenPolicy.Builder().showBadges(true).build();
+ normalDnd.setPolicy(somePolicy);
+ assertThat(normalDnd.getPolicy()).isEqualTo(somePolicy);
+
+ ZenDeviceEffects someEffects = new ZenDeviceEffects.Builder()
+ .setShouldUseNightMode(true).build();
+ normalDnd.setDeviceEffects(someEffects);
+ assertThat(normalDnd.getDeviceEffects()).isEqualTo(someEffects);
+ }
+
+ @Test
+ public void canEditPolicy_whenFalse_preventsSettingFilterPolicyOrEffects() {
+ ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_ALARMS, true);
+
+ assertThat(specialDnd.canEditPolicy()).isFalse();
+ assertThrows(IllegalStateException.class,
+ () -> specialDnd.setPolicy(ZEN_POLICY));
+ assertThrows(IllegalStateException.class,
+ () -> specialDnd.setDeviceEffects(new ZenDeviceEffects.Builder().build()));
+ }
+
+ @Test
public void comparator_prioritizes() {
ZenMode manualDnd = TestModeBuilder.MANUAL_DND_INACTIVE;
ZenMode driving1 = new TestModeBuilder().setName("b1").setType(TYPE_DRIVING).build();
@@ -359,7 +439,7 @@
}
@Test
- public void getIconKey_implicitModeWithoutCustomIcon_isSpecialIcon() {
+ public void getIconKey_implicitModeWithoutCustomIcon_isDndIcon() {
ZenMode mode = new TestModeBuilder()
.setId(ZenModeConfig.implicitRuleId("some.package"))
.setPackage("some_package")
@@ -370,7 +450,7 @@
assertThat(iconKey.resPackage()).isNull();
assertThat(iconKey.resId()).isEqualTo(
- com.android.internal.R.drawable.ic_zen_mode_type_unknown);
+ com.android.internal.R.drawable.ic_zen_mode_type_special_dnd);
}
private static void assertUnparceledIsEqualToOriginal(String type, ZenMode original) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
index 539519b..aae72b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
@@ -16,12 +16,14 @@
package com.android.settingslib.notification.modes;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.Condition.SOURCE_UNKNOWN;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.ZenAdapters.notificationPolicyToZenPolicy;
import static android.service.notification.ZenPolicy.STATE_ALLOW;
@@ -47,8 +49,6 @@
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
-import com.android.settingslib.R;
-
import com.google.common.collect.ImmutableMap;
import org.junit.Before;
@@ -172,9 +172,8 @@
// all modes exist, but none of them are currently active
assertThat(modes).containsExactly(
ZenMode.manualDndMode(
- new AutomaticZenRule.Builder(
- mContext.getString(R.string.zen_mode_settings_title),
- Uri.EMPTY)
+ new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
+ .setPackage(PACKAGE_ANDROID)
.setType(AutomaticZenRule.TYPE_OTHER)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(notificationPolicyToZenPolicy(dndPolicy))
@@ -196,15 +195,59 @@
ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
+ assertThat(mode).isNotNull();
assertThat(mode).isEqualTo(
ZenMode.manualDndMode(
- new AutomaticZenRule.Builder(
- mContext.getString(R.string.zen_mode_settings_title), Uri.EMPTY)
+ new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
+ .setPackage(PACKAGE_ANDROID)
.setType(AutomaticZenRule.TYPE_OTHER)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(notificationPolicyToZenPolicy(dndPolicy))
.setManualInvocationAllowed(true)
.build(), false));
+
+ assertThat(mode.isManualDnd()).isTrue();
+ assertThat(mode.isEnabled()).isTrue();
+ assertThat(mode.isActive()).isFalse();
+ assertThat(mode.canEditPolicy()).isTrue();
+ assertThat(mode.getPolicy()).isEqualTo(notificationPolicyToZenPolicy(dndPolicy));
+ }
+
+ @Test
+ public void getMode_dndWithOtherInterruptionFilter_returnsSpecialDndMode() {
+ ZenModeConfig config = configWithManualRule(new ZenModeConfig(), true);
+ config.manualRule.zenMode = Settings.Global.ZEN_MODE_ALARMS;
+ Policy dndPolicyForPriority = new Policy(Policy.PRIORITY_CATEGORY_ALARMS,
+ Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
+ config.applyNotificationPolicy(dndPolicyForPriority);
+ when(mNm.getZenModeConfig()).thenReturn(config);
+
+ ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
+
+ assertThat(mode).isNotNull();
+ assertThat(mode).isEqualTo(
+ ZenMode.manualDndMode(
+ new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
+ .setPackage(PACKAGE_ANDROID)
+ .setType(AutomaticZenRule.TYPE_OTHER)
+ .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+ .setZenPolicy(notificationPolicyToZenPolicy(dndPolicyForPriority))
+ .setManualInvocationAllowed(true)
+ .build(), true));
+
+ assertThat(mode.isManualDnd()).isTrue();
+ assertThat(mode.isEnabled()).isTrue();
+ assertThat(mode.isActive()).isTrue();
+
+ // Mode itself has a special fixed policy, different to the rule.
+ assertThat(mode.canEditPolicy()).isFalse();
+ assertThat(mode.getPolicy()).isEqualTo(
+ new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy())
+ .disallowAllSounds()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowPriorityChannels(false)
+ .build());
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index ca53fc2..3f3e1b2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -291,4 +291,12 @@
assertThat(mPreference.isApplyDynamicColor()).isTrue();
}
+
+ @Test
+ public void setContentDescription_getContentDescription_isEqual() {
+ final String contentDesc = "content desc";
+ mPreference.setContentDescription(contentDesc);
+
+ assertThat(mPreference.getContentDescription().toString()).isEqualTo(contentDesc);
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index ec50323..e85ba45 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2173,8 +2173,7 @@
(1 << AudioManager.STREAM_NOTIFICATION) |
(1 << AudioManager.STREAM_SYSTEM) |
(1 << AudioManager.STREAM_SYSTEM_ENFORCED);
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_voice_capable)) {
+ if (!getTelephonyManager().isVoiceCapable()) {
ringerModeAffectedStreams |= (1 << AudioManager.STREAM_MUSIC);
}
loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 121bd3e..bfbf41d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -99,6 +99,12 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print("SyncDisabledForTests: ");
+ MyShellCommand.getSyncDisabledForTests(pw, pw);
+
+ pw.print("Is mainline: ");
+ pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+
final IContentProvider iprovider = mProvider.getIContentProvider();
pw.println("DeviceConfig flags:");
for (String line : MyShellCommand.listAll(iprovider)) {
@@ -232,6 +238,17 @@
return Binder.getCallingUid() == Process.ROOT_UID;
}
+ private static int getSyncDisabledForTests(PrintWriter pOut, PrintWriter pErr) {
+ int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
+ String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
+ if (syncDisabledModeString == null) {
+ pErr.println("Unknown mode: " + syncDisabledModeInt);
+ return -1;
+ }
+ pOut.println(syncDisabledModeString);
+ return 0;
+ }
+
public static HashMap<String, String> getAllFlags(IContentProvider provider) {
HashMap<String, String> allFlags = new HashMap<String, String>();
for (DeviceConfig.Properties properties : DeviceConfig.getAllProperties()) {
@@ -597,14 +614,7 @@
DeviceConfig.setSyncDisabledMode(syncDisabledModeArg);
break;
case GET_SYNC_DISABLED_FOR_TESTS:
- int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
- String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
- if (syncDisabledModeString == null) {
- perr.println("Unknown mode: " + syncDisabledModeInt);
- return -1;
- }
- pout.println(syncDisabledModeString);
- break;
+ return getSyncDisabledForTests(pout, perr);
default:
perr.println("Unspecified command");
return -1;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0b5187c..f3c5a18 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -941,6 +941,13 @@
<!-- Permission required for CTS test - FileIntegrityManagerTest -->
<uses-permission android:name="android.permission.SETUP_FSVERITY" />
+ <!-- Permissions required for CTS test - AppFunctionManagerTest -->
+ <uses-permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" />
+ <uses-permission android:name="android.permission.EXECUTE_APP_FUNCTIONS" />
+
+ <!-- Permission required for CTS test - CtsNfcTestCases -->
+ <uses-permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index f0e5863..be4e9a1 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -756,6 +756,7 @@
"notification_flags_lib",
"PlatformComposeCore",
"PlatformComposeSceneTransitionLayout",
+ "PlatformComposeSceneTransitionLayoutTestsUtils",
"androidx.compose.runtime_runtime",
"androidx.compose.material3_material3",
"androidx.compose.material_material-icons-extended",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d394976..157af7d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -92,6 +92,7 @@
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.MASTER_CLEAR" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS" />
<uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
<uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
<uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index c60eb61..fb1f715 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -41,6 +41,7 @@
"SettingsLibDisplayUtils",
"SettingsLibSettingsTheme",
"com_android_a11y_menu_flags_lib",
+ "//frameworks/libs/systemui:view_capture",
],
optimize: {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant.xml
similarity index 89%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant.xml
index 9c3417f..6408a12 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down.xml
similarity index 92%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down.xml
index a64a0d1..f13239c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up.xml
similarity index 95%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up.xml
index 40423c7..a5d15f96 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock.xml
similarity index 90%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock.xml
index a0f7b5d..2127632 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications.xml
similarity index 90%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications.xml
index 8757f22..62c8d1d 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power.xml
similarity index 90%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power.xml
index 049013a..ed11b44 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings.xml
similarity index 97%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings.xml
index 4f25e7d..2da63a6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps.xml
similarity index 94%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps.xml
index 38234c0..9763b8e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot.xml
similarity index 95%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot.xml
index 6d7f49c..2bfbd5b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings.xml
similarity index 95%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings.xml
index 5ed6f19..4ca9bfc 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down.xml
similarity index 94%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down.xml
index 16653e8..f924e5e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up.xml
similarity index 95%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up.xml
index e572c6a..41fe351 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/res/color/brightness_slider_track.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/menuitem_background_ripple.xml
similarity index 79%
rename from packages/SystemUI/res/color/brightness_slider_track.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/menuitem_background_ripple.xml
index 6028769..6cab464 100644
--- a/packages/SystemUI/res/color/brightness_slider_track.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/menuitem_background_ripple.xml
@@ -14,6 +14,5 @@
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral2_500" android:lStar="40" />
-</selector>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/ripple_material_color" />
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index fd9a9c6..a1130e6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -3,8 +3,7 @@
android:id="@+id/shortcutItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingTop="@dimen/grid_item_padding"
- android:paddingBottom="@dimen/grid_item_padding"
+ android:padding="@dimen/grid_item_padding"
android:gravity="center">
<ImageButton
@@ -13,7 +12,8 @@
android:layout_height="@dimen/image_button_height"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
- android:scaleType="fitCenter"/>
+ android:scaleType="fitCenter"
+ android:background="@drawable/menuitem_background_ripple" />
<TextView
android:id="@+id/shortcutLabel"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
index 6be7655..adaa655 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
@@ -1,25 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
-<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/row_width"
android:layout_height="match_parent"
- android:id="@+id/coordinatorLayout"
- android:background="@drawable/view_background"
- >
- <LinearLayout
+ android:orientation="vertical"
+ android:background="@drawable/view_background">
+
+ <androidx.viewpager2.widget.ViewPager2
+ android:id="@+id/view_pager"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:paddingTop="@dimen/table_margin_top"
+ android:paddingBottom="@dimen/a11ymenu_layout_margin"
+ android:gravity="center"
+ />
- <androidx.viewpager2.widget.ViewPager2
- android:id="@+id/view_pager"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/table_margin_top"
- android:paddingBottom="@dimen/a11ymenu_layout_margin"
- android:layout_gravity="center"
- android:gravity="center"
- />
-
- <include layout="@layout/footerlayout_switch_page"/>
- </LinearLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
+ <include layout="@layout/footerlayout_switch_page"/>
+</LinearLayout>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
index c698d18..11ce41e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
@@ -53,73 +53,73 @@
/** Map stores all shortcut resource IDs that is in matching order of defined shortcut. */
private static final Map<ShortcutId, int[]> sShortcutResource = Map.ofEntries(
Map.entry(ShortcutId.ID_ASSISTANT_VALUE, new int[] {
- R.drawable.ic_logo_a11y_assistant_24dp,
+ R.drawable.ic_logo_a11y_assistant,
R.color.assistant_color,
R.string.assistant_utterance,
R.string.assistant_label,
}),
Map.entry(ShortcutId.ID_A11YSETTING_VALUE, new int[] {
- R.drawable.ic_logo_a11y_settings_24dp,
+ R.drawable.ic_logo_a11y_settings,
R.color.a11y_settings_color,
R.string.a11y_settings_label,
R.string.a11y_settings_label,
}),
Map.entry(ShortcutId.ID_POWER_VALUE, new int[] {
- R.drawable.ic_logo_a11y_power_24dp,
+ R.drawable.ic_logo_a11y_power,
R.color.power_color,
R.string.power_utterance,
R.string.power_label,
}),
Map.entry(ShortcutId.ID_RECENT_VALUE, new int[] {
- R.drawable.ic_logo_a11y_recent_apps_24dp,
+ R.drawable.ic_logo_a11y_recent_apps,
R.color.recent_apps_color,
R.string.recent_apps_label,
R.string.recent_apps_label,
}),
Map.entry(ShortcutId.ID_LOCKSCREEN_VALUE, new int[] {
- R.drawable.ic_logo_a11y_lock_24dp,
+ R.drawable.ic_logo_a11y_lock,
R.color.lockscreen_color,
R.string.lockscreen_label,
R.string.lockscreen_label,
}),
Map.entry(ShortcutId.ID_QUICKSETTING_VALUE, new int[] {
- R.drawable.ic_logo_a11y_quick_settings_24dp,
+ R.drawable.ic_logo_a11y_quick_settings,
R.color.quick_settings_color,
R.string.quick_settings_label,
R.string.quick_settings_label,
}),
Map.entry(ShortcutId.ID_NOTIFICATION_VALUE, new int[] {
- R.drawable.ic_logo_a11y_notifications_24dp,
+ R.drawable.ic_logo_a11y_notifications,
R.color.notifications_color,
R.string.notifications_label,
R.string.notifications_label,
}),
Map.entry(ShortcutId.ID_SCREENSHOT_VALUE, new int[] {
- R.drawable.ic_logo_a11y_screenshot_24dp,
+ R.drawable.ic_logo_a11y_screenshot,
R.color.screenshot_color,
R.string.screenshot_utterance,
R.string.screenshot_label,
}),
Map.entry(ShortcutId.ID_BRIGHTNESS_UP_VALUE, new int[] {
- R.drawable.ic_logo_a11y_brightness_up_24dp,
+ R.drawable.ic_logo_a11y_brightness_up,
R.color.brightness_color,
R.string.brightness_up_label,
R.string.brightness_up_label,
}),
Map.entry(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE, new int[] {
- R.drawable.ic_logo_a11y_brightness_down_24dp,
+ R.drawable.ic_logo_a11y_brightness_down,
R.color.brightness_color,
R.string.brightness_down_label,
R.string.brightness_down_label,
}),
Map.entry(ShortcutId.ID_VOLUME_UP_VALUE, new int[] {
- R.drawable.ic_logo_a11y_volume_up_24dp,
+ R.drawable.ic_logo_a11y_volume_up,
R.color.volume_color,
R.string.volume_up_label,
R.string.volume_up_label,
}),
Map.entry(ShortcutId.ID_VOLUME_DOWN_VALUE, new int[] {
- R.drawable.ic_logo_a11y_volume_down_24dp,
+ R.drawable.ic_logo_a11y_volume_down,
R.color.volume_color,
R.string.volume_down_label,
R.string.volume_down_label,
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java
deleted file mode 100644
index 28ba4b5..0000000
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2022 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.accessibilitymenu.utils;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.RippleDrawable;
-
-import com.android.systemui.accessibility.accessibilitymenu.R;
-
-/** Creates background drawable for a11y menu shortcut. */
-public class ShortcutDrawableUtils {
-
- /**
- * To make the circular background of shortcut icons have higher resolution. The higher value of
- * LENGTH is, the higher resolution of the circular background are.
- */
- private static final int LENGTH = 480;
-
- private static final int RADIUS = LENGTH / 2;
- private static final int COORDINATE = LENGTH / 2;
- private static final int RIPPLE_COLOR_ID = R.color.ripple_material_color;
-
- private final Context mContext;
- private final ColorStateList mRippleColorStateList;
-
- // Placeholder of drawable to prevent NullPointerException
- private final ColorDrawable mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT);
-
- public ShortcutDrawableUtils(Context context) {
- this.mContext = context;
-
- int rippleColor = context.getColor(RIPPLE_COLOR_ID);
- mRippleColorStateList = ColorStateList.valueOf(rippleColor);
- }
-
- /**
- * Creates a circular drawable in specific color for shortcut.
- *
- * @param colorResId color resource ID
- * @return drawable circular drawable
- */
- public Drawable createCircularDrawable(int colorResId) {
- Bitmap output = Bitmap.createBitmap(LENGTH, LENGTH, Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
- int color = mContext.getColor(colorResId);
- Paint paint = new Paint();
- paint.setColor(color);
- paint.setStrokeCap(Paint.Cap.ROUND);
- paint.setStyle(Style.FILL);
- canvas.drawCircle(COORDINATE, COORDINATE, RADIUS, paint);
-
- BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), output);
- return drawable;
- }
-
- /**
- * Creates an adaptive icon drawable in specific color for shortcut.
- *
- * @param colorResId color resource ID
- * @return drawable for adaptive icon
- */
- public Drawable createAdaptiveIconDrawable(int colorResId) {
- Drawable circleLayer = createCircularDrawable(colorResId);
- RippleDrawable rippleLayer = new RippleDrawable(mRippleColorStateList, null, null);
-
- AdaptiveIconDrawable adaptiveIconDrawable =
- new AdaptiveIconDrawable(circleLayer, mTransparentDrawable);
-
- Drawable[] layers = {adaptiveIconDrawable, rippleLayer};
- LayerDrawable drawable = new LayerDrawable(layers);
- return drawable;
- }
-}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
index c2cf6e1..aa1bbbd 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
@@ -16,8 +16,12 @@
package com.android.systemui.accessibility.accessibilitymenu.view;
-import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
import android.view.LayoutInflater;
import android.view.TouchDelegate;
import android.view.View;
@@ -27,11 +31,12 @@
import android.widget.ImageButton;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.R;
import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
-import com.android.systemui.accessibility.accessibilitymenu.utils.ShortcutDrawableUtils;
import java.util.List;
@@ -43,19 +48,13 @@
private final int mLargeTextSize;
private final AccessibilityMenuService mService;
- private final LayoutInflater mInflater;
private final List<A11yMenuShortcut> mShortcutDataList;
- private final ShortcutDrawableUtils mShortcutDrawableUtils;
public A11yMenuAdapter(
AccessibilityMenuService service,
- Context displayContext, List<A11yMenuShortcut> shortcutDataList) {
+ List<A11yMenuShortcut> shortcutDataList) {
this.mService = service;
this.mShortcutDataList = shortcutDataList;
- mInflater = LayoutInflater.from(displayContext);
-
- mShortcutDrawableUtils = new ShortcutDrawableUtils(service);
-
mLargeTextSize =
service.getResources().getDimensionPixelOffset(R.dimen.large_label_text_size);
}
@@ -78,7 +77,8 @@
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
- convertView = mInflater.inflate(R.layout.grid_item, parent, false);
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.grid_item, parent, false);
configureShortcutSize(convertView,
A11yMenuPreferenceFragment.isLargeButtonsEnabled(mService));
@@ -154,10 +154,10 @@
shortcutIconButton.setContentDescription(
mService.getString(shortcutItem.imgContentDescription));
shortcutLabel.setText(shortcutItem.labelText);
- shortcutIconButton.setImageResource(shortcutItem.imageSrc);
- shortcutIconButton.setBackground(
- mShortcutDrawableUtils.createAdaptiveIconDrawable(shortcutItem.imageColor));
+ AdaptiveIconDrawable iconDrawable = getAdaptiveIconDrawable(convertView,
+ shortcutItem);
+ shortcutIconButton.setImageDrawable(iconDrawable);
shortcutIconButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
@@ -169,4 +169,18 @@
});
}
}
+
+ @NonNull
+ private static AdaptiveIconDrawable getAdaptiveIconDrawable(@NonNull View convertView,
+ @NonNull A11yMenuShortcut shortcutItem) {
+ Resources resources = convertView.getResources();
+ // Note: from the official guide, the foreground image of the adaptive icon should be
+ // sized at 108 x 108 dp
+ Drawable icon = resources.getDrawable(shortcutItem.imageSrc);
+ float inset = AdaptiveIconDrawable.getExtraInsetFraction();
+ AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable(
+ new ColorDrawable(resources.getColor(shortcutItem.imageColor)),
+ new InsetDrawable(icon, inset));
+ return iconDrawable;
+ }
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
index 78fbf01..136a4ed 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
@@ -55,6 +55,8 @@
private View mBottomListDivider;
private final A11yMenuFooterCallBack mCallBack;
private final ViewGroup mMenuLayout;
+ private ViewGroup mFooterContainer;
+ private int mFooterContainerBaseHeight = 0;
private int mRightToLeftDirection = LAYOUT_DIRECTION_LTR;
public A11yMenuFooter(ViewGroup menuLayout, A11yMenuFooterCallBack callBack) {
@@ -74,6 +76,15 @@
? mPageRightBtn : mPageLeftBtn;
}
+ void adjustFooterToDensityScale(float densityScale) {
+ mFooterContainer.getLayoutParams().height =
+ (int) (mFooterContainerBaseHeight / densityScale);
+ }
+
+ int getHeight() {
+ return mFooterContainer.getLayoutParams().height;
+ }
+
/** Sets right to left direction of footer. */
public void updateRightToLeftDirection(Configuration configuration) {
mRightToLeftDirection = TextUtils.getLayoutDirectionFromLocale(
@@ -85,8 +96,9 @@
}
private void configureFooterLayout(ViewGroup menuLayout) {
- ViewGroup footerContainer = menuLayout.findViewById(R.id.footerlayout);
- footerContainer.setVisibility(View.VISIBLE);
+ mFooterContainer = menuLayout.findViewById(R.id.footerlayout);
+ mFooterContainer.setVisibility(View.VISIBLE);
+ mFooterContainerBaseHeight = mFooterContainer.getLayoutParams().height;
mPageLeftBtn = menuLayout.findViewById(R.id.menu_left_button);
mPageRightBtn = menuLayout.findViewById(R.id.menu_right_button);
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index de3c472..3db61a5 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -22,6 +22,8 @@
import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
+
import static java.lang.Math.max;
import android.animation.Animator;
@@ -51,7 +53,9 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.UiContext;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.Flags;
import com.android.systemui.accessibility.accessibilitymenu.R;
@@ -101,7 +105,6 @@
};
private final AccessibilityMenuService mService;
- private final WindowManager mWindowManager;
private final DisplayManager mDisplayManager;
private ViewGroup mLayout;
private WindowManager.LayoutParams mLayoutParameter;
@@ -111,7 +114,6 @@
public A11yMenuOverlayLayout(AccessibilityMenuService service) {
mService = service;
- mWindowManager = mService.getSystemService(WindowManager.class);
mDisplayManager = mService.getSystemService(DisplayManager.class);
configureLayout();
mHandler = new Handler(Looper.getMainLooper());
@@ -134,8 +136,7 @@
int lastVisibilityState = View.GONE;
if (mLayout != null) {
lastVisibilityState = mLayout.getVisibility();
- mWindowManager.removeView(mLayout);
- mLayout = null;
+ clearLayout();
}
if (mLayoutParameter == null) {
@@ -143,14 +144,17 @@
}
final Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
- final Context context = mService.createDisplayContext(display).createWindowContext(
- TYPE_ACCESSIBILITY_OVERLAY, null);
- mLayout = new A11yMenuFrameLayout(context);
- updateLayoutPosition();
- inflateLayoutAndSetOnTouchListener(mLayout, context);
- mA11yMenuViewPager = new A11yMenuViewPager(mService, context);
+ final Context uiContext = mService.createWindowContext(
+ display, TYPE_ACCESSIBILITY_OVERLAY, /* options= */null);
+ final ViewCaptureAwareWindowManager windowManager =
+ getViewCaptureAwareWindowManagerInstance(uiContext,
+ com.android.systemui.Flags.enableViewCaptureTracing());
+ mLayout = new A11yMenuFrameLayout(uiContext);
+ updateLayoutPosition(uiContext);
+ inflateLayoutAndSetOnTouchListener(mLayout, uiContext);
+ mA11yMenuViewPager = new A11yMenuViewPager(mService);
mA11yMenuViewPager.configureViewPagerAndFooter(mLayout, createShortcutList(), pageIndex);
- mWindowManager.addView(mLayout, mLayoutParameter);
+ windowManager.addView(mLayout, mLayoutParameter);
mLayout.setVisibility(lastVisibilityState);
mA11yMenuViewPager.updateFooterState();
@@ -159,7 +163,11 @@
public void clearLayout() {
if (mLayout != null) {
- mWindowManager.removeView(mLayout);
+ ViewCaptureAwareWindowManager windowManager = getViewCaptureAwareWindowManagerInstance(
+ mLayout.getContext(), com.android.systemui.Flags.enableViewCaptureTracing());
+ if (windowManager != null) {
+ windowManager.removeView(mLayout);
+ }
mLayout.setOnTouchListener(null);
mLayout = null;
}
@@ -170,8 +178,11 @@
if (mLayout == null || mLayoutParameter == null) {
return;
}
- updateLayoutPosition();
- mWindowManager.updateViewLayout(mLayout, mLayoutParameter);
+ updateLayoutPosition(mLayout.getContext());
+ WindowManager windowManager = mLayout.getContext().getSystemService(WindowManager.class);
+ if (windowManager != null) {
+ windowManager.updateViewLayout(mLayout, mLayoutParameter);
+ }
}
private void initLayoutParams() {
@@ -183,8 +194,8 @@
mLayoutParameter.setTitle(mService.getString(R.string.accessibility_menu_service_name));
}
- private void inflateLayoutAndSetOnTouchListener(ViewGroup view, Context displayContext) {
- LayoutInflater inflater = LayoutInflater.from(displayContext);
+ private void inflateLayoutAndSetOnTouchListener(ViewGroup view, @UiContext Context uiContext) {
+ LayoutInflater inflater = LayoutInflater.from(uiContext);
inflater.inflate(R.layout.paged_menu, view);
view.setOnTouchListener(mService);
}
@@ -238,7 +249,11 @@
}
/** Updates a11y menu layout position by configuring layout params. */
- private void updateLayoutPosition() {
+ private void updateLayoutPosition(@UiContext @NonNull Context uiContext) {
+ WindowManager windowManager = uiContext.getSystemService(WindowManager.class);
+ if (windowManager == null) {
+ return;
+ }
final Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
final Configuration configuration = mService.getResources().getConfiguration();
final int orientation = configuration.orientation;
@@ -276,14 +291,13 @@
mLayoutParameter.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLayout.setBackgroundResource(R.drawable.shadow_0deg);
}
-
// Adjusts the y position of a11y menu layout to make the layout not to overlap bottom
// navigation bar window.
- updateLayoutByWindowInsetsIfNeeded();
+ updateLayoutByWindowInsetsIfNeeded(windowManager);
mLayout.setOnApplyWindowInsetsListener(
(view, insets) -> {
- if (updateLayoutByWindowInsetsIfNeeded()) {
- mWindowManager.updateViewLayout(mLayout, mLayoutParameter);
+ if (updateLayoutByWindowInsetsIfNeeded(windowManager)) {
+ windowManager.updateViewLayout(mLayout, mLayoutParameter);
}
return view.onApplyWindowInsets(insets);
});
@@ -295,9 +309,9 @@
* This method adjusts the layout position and size to
* make a11y menu not to overlap navigation bar window.
*/
- private boolean updateLayoutByWindowInsetsIfNeeded() {
+ private boolean updateLayoutByWindowInsetsIfNeeded(@NonNull WindowManager windowManager) {
boolean shouldUpdateLayout = false;
- WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
+ WindowMetrics windowMetrics = windowManager.getCurrentWindowMetrics();
Insets windowInsets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
int xOffset = max(windowInsets.left, windowInsets.right);
@@ -396,7 +410,7 @@
}
private class A11yMenuFrameLayout extends FrameLayout {
- A11yMenuFrameLayout(@NonNull Context context) {
+ A11yMenuFrameLayout(@UiContext @NonNull Context context) {
super(context);
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
index 08bbf19..b899c45 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
@@ -28,6 +28,7 @@
import android.view.WindowMetrics;
import android.widget.GridView;
+import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
@@ -146,12 +147,8 @@
/** The container layout for a11y menu. */
private ViewGroup mA11yMenuLayout;
- /** Display context for inflating views. */
- private Context mDisplayContext;
-
- public A11yMenuViewPager(AccessibilityMenuService service, Context displayContext) {
+ public A11yMenuViewPager(AccessibilityMenuService service) {
this.mService = service;
- this.mDisplayContext = displayContext;
}
/**
@@ -167,7 +164,9 @@
mA11yMenuShortcutList = shortcutDataList;
initViewPager();
initChildPage();
- mA11yMenuFooter = new A11yMenuFooter(a11yMenuLayout, mFooterCallbacks);
+ if (mA11yMenuFooter == null) {
+ mA11yMenuFooter = new A11yMenuFooter(a11yMenuLayout, mFooterCallbacks);
+ }
mA11yMenuFooter.updateRightToLeftDirection(
a11yMenuLayout.getResources().getConfiguration());
updateFooterState();
@@ -237,11 +236,17 @@
return;
}
- if (mGridPageList.isEmpty()) {
+ if (mViewPagerAdapter.getItemCount() == 0) {
return;
}
- GridView firstGridView = mGridPageList.get(0);
+ RecyclerView.ViewHolder viewHolder =
+ ((RecyclerView) mViewPager.getChildAt(0))
+ .findViewHolderForAdapterPosition(0);
+ if (viewHolder == null) {
+ return;
+ }
+ GridView firstGridView = (GridView) viewHolder.itemView;
if (firstGridView == null
|| firstGridView.getChildAt(0) == null) {
return;
@@ -284,31 +289,32 @@
DisplayMetrics displayMetrics = mService.getResources().getDisplayMetrics();
float densityScale = (float) displayMetrics.densityDpi
/ DisplayMetrics.DENSITY_DEVICE_STABLE;
- View footerLayout = mA11yMenuLayout.findViewById(R.id.footerlayout);
// Keeps footer window height unchanged no matter the density is changed.
- footerLayout.getLayoutParams().height =
- (int) (footerLayout.getLayoutParams().height / densityScale);
+ mA11yMenuFooter.adjustFooterToDensityScale(densityScale);
// Adjust the view pager height for system bar and display cutout insets.
- WindowManager windowManager = mService.getSystemService(WindowManager.class);
+ WindowManager windowManager = mA11yMenuLayout.getContext()
+ .getSystemService(WindowManager.class);
WindowMetrics windowMetric = windowManager.getCurrentWindowMetrics();
Insets windowInsets = windowMetric.getWindowInsets().getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
viewPagerHeight =
windowMetric.getBounds().height()
- - footerLayout.getLayoutParams().height
+ - mA11yMenuFooter.getHeight()
- windowInsets.bottom;
// Sets vertical interval between grid items.
int interval =
(viewPagerHeight - topMargin - defaultMargin
- (rowsInGridView * gridItemHeight))
/ (rowsInGridView + 1);
- for (GridView gridView : mGridPageList) {
- gridView.setVerticalSpacing(interval);
- }
+ // The interval is negative number when the viewPagerHeight is not able to fit
+ // the grid items, which result in text overlapping.
+ // Adjust the interval to 0 could solve the issue.
+ interval = Math.max(interval, 0);
+ mViewPagerAdapter.setVerticalSpacing(interval);
// Sets padding to view pager.
final int finalMarginTop = interval + topMargin;
- mViewPager.setPadding(defaultMargin, finalMarginTop, defaultMargin, defaultMargin);
+ mViewPager.setPadding(0, finalMarginTop, 0, defaultMargin);
}
final ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams();
layoutParams.height = viewPagerHeight;
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
index 43ec956..4b14e51 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
@@ -36,11 +36,19 @@
/** List of shortcuts, split into sub lists per page */
private List<List<A11yMenuShortcut>> mShortcutList;
private final AccessibilityMenuService mService;
+ private int mVerticalSpacing = 0;
ViewPagerAdapter(AccessibilityMenuService service) {
mService = service;
}
+ public void setVerticalSpacing(int spacing) {
+ if (mVerticalSpacing != spacing) {
+ mVerticalSpacing = spacing;
+ notifyDataSetChanged();
+ }
+ }
+
public void set(List<List<A11yMenuShortcut>> tList) {
mShortcutList = tList;
notifyDataSetChanged();
@@ -57,10 +65,11 @@
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
A11yMenuAdapter adapter = new A11yMenuAdapter(
- mService, holder.itemView.getContext(), mShortcutList.get(position));
+ mService, mShortcutList.get(position));
GridView gridView = (GridView) holder.itemView;
gridView.setNumColumns(A11yMenuViewPager.GridViewParams.getGridColumnCount(mService));
gridView.setAdapter(adapter);
+ gridView.setVerticalSpacing(mVerticalSpacing);
}
@Override
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 97206de..02e8cd6 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -247,13 +247,6 @@
}
flag {
- name: "haptic_brightness_slider"
- namespace: "systemui"
- description: "Adds haptic feedback to the brightness slider."
- bug: "296467915"
-}
-
-flag {
name: "unfold_animation_background_progress"
namespace: "systemui"
description: "Moves unfold animation progress calculation to a background thread"
@@ -288,6 +281,16 @@
}
flag {
+ name: "qs_quick_rebind_active_tiles"
+ namespace: "systemui"
+ description: "Rebind active custom tiles quickly."
+ bug: "362526228"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "coroutine_tracing"
namespace: "systemui"
description: "Adds thread-local data to System UI's global coroutine scopes to "
@@ -606,26 +609,6 @@
}
flag {
- name: "screenshot_private_profile_behavior_fix"
- namespace: "systemui"
- description: "Private profile support for screenshots"
- bug: "327613051"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "screenshot_save_image_exporter"
- namespace: "systemui"
- description: "Save all screenshots using ImageExporter"
- bug: "352308052"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "screenshot_ui_controller_refactor"
namespace: "systemui"
description: "Simplify and refactor ScreenshotController"
@@ -647,10 +630,13 @@
}
flag {
- name: "smartspace_remoteviews_rendering"
+ name: "smartspace_remoteviews_rendering_fix"
namespace: "systemui"
description: "Indicate Smartspace RemoteViews rendering"
bug: "326292691"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -661,6 +647,13 @@
}
flag {
+ name: "smartspace_viewpager2"
+ namespace: "systemui"
+ description: "Use viewpager2 in Smartspace"
+ bug: "259566300"
+}
+
+flag {
name: "pin_input_field_styled_focus_state"
namespace: "systemui"
description: "Enables styled focus states on pin input field if keyboard is connected"
@@ -1340,15 +1333,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."
@@ -1366,13 +1350,6 @@
}
flag {
- name: "new_picker_ui"
- namespace: "systemui"
- description: "Enables the BC25 design of the customization picker UI."
- bug: "339081035"
-}
-
-flag {
namespace: "systemui"
name: "settings_ext_register_content_observer_on_bg_thread"
description: "Register content observer in callback flow APIs on background thread in SettingsProxyExt."
@@ -1383,6 +1360,16 @@
}
flag {
+ name: "notify_password_text_view_user_activity_in_background"
+ namespace: "systemui"
+ description: "Decide whether to notify the user activity in password text view, to power manager in the background thread."
+ bug: "346882515"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "face_message_defer_update"
namespace: "systemui"
description: "Only analyze the last n frames when determining whether to defer a face auth help message like low light"
@@ -1402,3 +1389,9 @@
}
}
+flag {
+ name: "non_touchscreen_devices_bypass_falsing"
+ namespace: "systemui"
+ description: "Allow non-touchscreen devices to bypass falsing"
+ bug: "319809270"
+}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
index 2b1268e..5b368df 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.bouncer.ui.composable.BouncerScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
index 94b5db2..74ce4bb 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.communal.ui.compose.CommunalScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
index bc3fef1..871ade9 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
@@ -16,8 +16,8 @@
package com.android.systemui.scene
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.ui.composable.GoneScene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index 72965fb..bfeaf92 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -27,7 +27,7 @@
import com.android.systemui.keyguard.ui.composable.LockscreenSceneBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.Provides
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeOverlayModule.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
copy to packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeOverlayModule.kt
index 7e09a10..e55520a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeOverlayModule.kt
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard
+package com.android.systemui.scene
-import com.android.systemui.Flags
+import com.android.systemui.notifications.ui.composable.NotificationsShadeOverlay
+import com.android.systemui.scene.ui.composable.Overlay
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
-/** Helper for reading or using the new picker UI flag. */
-@Suppress("NOTHING_TO_INLINE")
-object NewPickerUiKeyguardPreview {
+@Module
+interface NotificationsShadeOverlayModule {
- /** Is the new picker UI enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.newPickerUi()
+ @Binds @IntoSet fun notificationsShade(overlay: NotificationsShadeOverlay): Overlay
}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
index 9b736b8..c58df35 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.notifications.ui.composable.NotificationsShadeScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
index ee1f525..d55210d 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.qs.ui.composable.QuickSettingsScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
copy to packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.kt
index 7e09a10..bc4adf9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.kt
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard
+package com.android.systemui.scene
-import com.android.systemui.Flags
+import com.android.systemui.qs.ui.composable.QuickSettingsShadeOverlay
+import com.android.systemui.scene.ui.composable.Overlay
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
-/** Helper for reading or using the new picker UI flag. */
-@Suppress("NOTHING_TO_INLINE")
-object NewPickerUiKeyguardPreview {
+@Module
+interface QuickSettingsShadeOverlayModule {
- /** Is the new picker UI enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.newPickerUi()
+ @Binds @IntoSet fun quickSettingsShade(overlay: QuickSettingsShadeOverlay): Overlay
}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
index 3d7401d..5bb6ae4 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.qs.ui.composable.QuickSettingsShadeScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
index c655d6b..186914f 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
@@ -16,7 +16,7 @@
package com.android.systemui.scene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.ui.composable.ShadeScene
import dagger.Binds
import dagger.Module
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index d164eab..34eafde 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -102,6 +102,7 @@
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.fold.ui.composable.foldPosture
import com.android.systemui.fold.ui.helper.FoldPosture
import com.android.systemui.res.R
@@ -259,8 +260,11 @@
viewModel = viewModel.message,
modifier = Modifier.align(Alignment.TopCenter),
)
-
- OutputArea(viewModel = viewModel, modifier = Modifier.align(Alignment.Center))
+ OutputArea(
+ viewModel = viewModel,
+ modifier =
+ Modifier.align(Alignment.Center).sysuiResTag("bouncer_text_entry")
+ )
ActionArea(
viewModel = viewModel,
@@ -310,8 +314,11 @@
StatusMessage(
viewModel = viewModel.message,
)
-
- OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp))
+ OutputArea(
+ viewModel = viewModel,
+ modifier =
+ Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry")
+ )
}
}
else -> Unit
@@ -417,10 +424,9 @@
StatusMessage(
viewModel = viewModel.message,
)
-
OutputArea(
viewModel = viewModel,
- modifier = Modifier.padding(top = 24.dp).testTag("OutputArea")
+ modifier = Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry")
)
}
},
@@ -485,7 +491,6 @@
StatusMessage(
viewModel = viewModel.message,
)
-
OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp))
InputArea(
@@ -528,7 +533,7 @@
// Update state whenever currentSceneKey has changed.
LaunchedEffect(state, currentSceneKey) {
if (currentSceneKey != state.transitionState.currentScene) {
- state.setTargetScene(currentSceneKey, coroutineScope = this)
+ state.setTargetScene(currentSceneKey, animationScope = this)
}
}
@@ -654,17 +659,16 @@
) {
val authMethodViewModel: AuthMethodBouncerViewModel? by
viewModel.authMethodViewModel.collectAsStateWithLifecycle()
-
when (val nonNullViewModel = authMethodViewModel) {
is PinBouncerViewModel ->
PinInputDisplay(
viewModel = nonNullViewModel,
- modifier = modifier,
+ modifier = modifier.sysuiResTag("bouncer_text_entry")
)
is PasswordBouncerViewModel ->
PasswordBouncer(
viewModel = nonNullViewModel,
- modifier = modifier,
+ modifier = modifier.sysuiResTag("bouncer_text_entry")
)
else -> Unit
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 270d751..ae92d259 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -27,14 +27,14 @@
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneActionsViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerUserActionsViewModel
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -54,18 +54,17 @@
class BouncerScene
@Inject
constructor(
- private val actionsViewModelFactory: BouncerSceneActionsViewModel.Factory,
+ private val actionsViewModelFactory: BouncerUserActionsViewModel.Factory,
private val contentViewModelFactory: BouncerSceneContentViewModel.Factory,
private val dialogFactory: BouncerDialogFactory,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Bouncer
- private val actionsViewModel: BouncerSceneActionsViewModel by lazy {
+ private val actionsViewModel: BouncerUserActionsViewModel by lazy {
actionsViewModelFactory.create()
}
- override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- actionsViewModel.actions
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
override suspend fun onActivated(): Nothing {
actionsViewModel.activate()
@@ -100,8 +99,8 @@
BouncerContent(
viewModel,
dialogFactory,
- Modifier.sysuiResTag(Bouncer.TestTags.Root)
- .element(Bouncer.Elements.Content)
+ Modifier.element(Bouncer.Elements.Content)
+ .sysuiResTag(Bouncer.TestTags.Root)
.fillMaxSize()
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 4ec0d99..480e4e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -93,11 +93,15 @@
}
}
+ // set the focus, so adb can send the key events for testing.
+ val focusRequester = FocusRequester()
+ LaunchedEffect(Unit) { focusRequester.requestFocus() }
+
VerticalGrid(
columns = columns,
verticalSpacing = verticalSpacing,
horizontalSpacing = calculateHorizontalSpacingBetweenColumns(gridWidth = 300.dp),
- modifier = modifier,
+ modifier = modifier.focusRequester(focusRequester)
) {
repeat(9) { index ->
DigitButton(
@@ -285,26 +289,26 @@
Box(
contentAlignment = Alignment.Center,
modifier =
- modifier
- .focusRequester(FocusRequester.Default)
- .focusable()
- .sizeIn(maxWidth = pinButtonMaxSize, maxHeight = pinButtonMaxSize)
- .aspectRatio(1f)
- .drawBehind {
- drawRoundRect(
- color = containerColor,
- cornerRadius = CornerRadius(cornerRadius.toPx()),
- )
- }
- .clip(CircleShape)
- .thenIf(isEnabled) {
- Modifier.combinedClickable(
- interactionSource = interactionSource,
- indication = indication,
- onClick = onClicked,
- onLongClick = onLongPressed
- )
- },
+ modifier
+ .focusRequester(FocusRequester.Default)
+ .focusable()
+ .sizeIn(maxWidth = pinButtonMaxSize, maxHeight = pinButtonMaxSize)
+ .aspectRatio(1f)
+ .drawBehind {
+ drawRoundRect(
+ color = containerColor,
+ cornerRadius = CornerRadius(cornerRadius.toPx()),
+ )
+ }
+ .clip(CircleShape)
+ .thenIf(isEnabled) {
+ Modifier.combinedClickable(
+ interactionSource = interactionSource,
+ indication = indication,
+ onClick = onClicked,
+ onLongClick = onLongPressed
+ )
+ },
) {
content(contentColor::value)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 465eade..1f98cd8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -34,8 +34,10 @@
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -63,7 +65,6 @@
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformOutlinedButton
import com.android.compose.animation.Easings
@@ -351,14 +352,20 @@
@Composable
fun Content(modifier: Modifier) {
- Row(
- modifier =
+
+ // Wrap PIN entry in a Box so it is visible to accessibility (even if empty).
+ Box(
+ modifier = modifier.fillMaxWidth().wrapContentHeight(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Row(
modifier
.heightIn(min = shapeAnimations.shapeSize)
// Pins overflowing horizontally should still be shown as scrolling.
- .wrapContentSize(unbounded = true),
- ) {
- entries.forEach { entry -> key(entry.digit) { entry.Content() } }
+ .wrapContentSize(unbounded = true)
+ ) {
+ entries.forEach { entry -> key(entry.digit) { entry.Content() } }
+ }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt
index e1f73e3..4e8121f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt
@@ -17,9 +17,12 @@
package com.android.systemui.common.ui.compose
+import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.shared.model.Text.Companion.loadText
/** Returns the loaded [String] or `null` if there isn't one. */
@Composable
@@ -29,3 +32,7 @@
is Text.Resource -> stringResource(res)
}
}
+
+fun Text.toAnnotatedString(context: Context): AnnotatedString? {
+ return loadText(context)?.let { AnnotatedString(it) }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index ed12776..a4dc8fc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -216,7 +216,7 @@
/** Scene containing the glanceable hub UI. */
@Composable
-private fun SceneScope.CommunalScene(
+fun SceneScope.CommunalScene(
backgroundType: CommunalBackgroundType,
colors: CommunalColors,
content: CommunalContent,
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..c63b29d 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
@@ -65,6 +65,7 @@
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
+import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.selectable
@@ -136,6 +137,7 @@
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
@@ -196,7 +198,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 +226,6 @@
val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
val screenWidth = windowMetrics.bounds.width()
val layoutDirection = LocalLayoutDirection.current
-
if (viewModel.isEditMode) {
ObserveNewWidgetAddedEffect(communalContent, gridState, viewModel)
} else {
@@ -236,10 +242,15 @@
}
}
+ val paneTitle = stringResource(R.string.accessibility_content_description_for_communal_hub)
+
Box(
modifier =
modifier
- .semantics { testTagsAsResourceId = true }
+ .semantics {
+ testTagsAsResourceId = true
+ this.paneTitle = paneTitle
+ }
.testTag(COMMUNAL_HUB_TEST_TAG)
.fillMaxSize()
// Observe taps for selecting items
@@ -543,7 +554,6 @@
communalContent: List<CommunalContentModel>,
gridState: LazyGridState,
) {
- val coroutineScope = rememberCoroutineScope()
val liveContentKeys = remember { mutableListOf<String>() }
var communalContentPending by remember { mutableStateOf(true) }
@@ -687,21 +697,20 @@
horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
) {
- items(
- count = list.size,
- key = { index -> list[index].key },
- contentType = { index -> list[index].key },
- span = { index -> GridItemSpan(list[index].size.span) },
- ) { index ->
+ itemsIndexed(
+ items = list,
+ key = { _, item -> item.key },
+ contentType = { _, item -> item.key },
+ span = { _, item -> GridItemSpan(item.size.span) },
+ ) { index, item ->
val size =
SizeF(
Dimensions.CardWidth.value,
- list[index].size.dp().value,
+ item.size.dp().value,
)
val cardModifier = Modifier.requiredSize(width = size.width.dp, height = size.height.dp)
if (viewModel.isEditMode && dragDropState != null) {
- val selected by
- remember(index) { derivedStateOf { list[index].key == selectedKey.value } }
+ val selected = item.key == selectedKey.value
DraggableItem(
modifier =
if (dragDropState.draggingItemIndex == index) {
@@ -713,12 +722,12 @@
},
dragDropState = dragDropState,
selected = selected,
- enabled = list[index].isWidgetContent(),
+ enabled = item.isWidgetContent(),
index = index,
) { isDragging ->
CommunalContent(
modifier = cardModifier,
- model = list[index],
+ model = item,
viewModel = viewModel,
size = size,
selected = selected && !isDragging,
@@ -731,7 +740,7 @@
}
} else {
CommunalContent(
- model = list[index],
+ model = item,
viewModel = viewModel,
size = size,
selected = false,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 54ffcf4..e41a7df 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -17,20 +17,21 @@
package com.android.systemui.communal.ui.compose
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetInteractionHandler
+import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import com.android.systemui.scene.ui.composable.Scene
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
@@ -43,16 +44,15 @@
@Inject
constructor(
private val viewModel: CommunalViewModel,
- private val dialogFactory: SystemUIDialogFactory,
- private val interactionHandler: WidgetInteractionHandler,
- private val widgetSection: CommunalAppWidgetSection,
-) : ExclusiveActivatable(), ComposableScene {
+ private val communalColors: CommunalColors,
+ private val communalContent: CommunalContent,
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Communal
- override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- MutableStateFlow<Map<UserAction, UserActionResult>>(
+ override val userActions: Flow<Map<UserAction, UserActionResult>> =
+ MutableStateFlow(
mapOf(
- Swipe(SwipeDirection.End) to UserActionResult(Scenes.Lockscreen),
+ Swipe(SwipeDirection.End) to Scenes.Lockscreen,
)
)
.asStateFlow()
@@ -63,12 +63,17 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- CommunalHub(
- modifier = modifier,
+ val backgroundType by
+ viewModel.communalBackground.collectAsStateWithLifecycle(
+ initialValue = CommunalBackgroundType.ANIMATED
+ )
+
+ CommunalScene(
+ backgroundType = backgroundType,
+ colors = communalColors,
+ content = communalContent,
viewModel = viewModel,
- interactionHandler = interactionHandler,
- widgetSection = widgetSection,
- dialogFactory = dialogFactory,
+ modifier = modifier.horizontalNestedScrollToScene(),
)
}
}
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 04bcc36..c25a45d 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
@@ -53,6 +52,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
+import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.res.R
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -73,9 +73,6 @@
initialValue = null
)
- // TODO (b/353955910): back handling doesn't work
- BackHandler { alternateBouncerDependencies.viewModel.onBackRequested() }
-
AnimatedVisibility(
visible = isVisible,
enter = fadeIn(),
@@ -101,6 +98,7 @@
Box {
DeviceEntryIcon(
viewModel = alternateBouncerDependencies.udfpsIconViewModel,
+ logger = alternateBouncerDependencies.logger,
modifier =
Modifier.width { udfpsLocation.width }
.height { udfpsLocation.height }
@@ -155,13 +153,14 @@
@Composable
private fun DeviceEntryIcon(
viewModel: AlternateBouncerUdfpsIconViewModel,
+ logger: LongPressHandlingViewLogger,
modifier: Modifier = Modifier,
) {
AndroidView(
modifier = modifier,
factory = { context ->
val view =
- DeviceEntryIconView(context, null).apply {
+ DeviceEntryIconView(context, null, logger = logger).apply {
id = R.id.alternate_bouncer_udfps_icon_view
contentDescription =
context.resources.getString(R.string.accessibility_fingerprint_label)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 2029e9e..c7c29f9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -22,13 +22,13 @@
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.animateContentFloatAsState
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneActionsViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenUserActionsViewModel
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -38,17 +38,16 @@
class LockscreenScene
@Inject
constructor(
- actionsViewModelFactory: LockscreenSceneActionsViewModel.Factory,
+ actionsViewModelFactory: LockscreenUserActionsViewModel.Factory,
private val lockscreenContent: Lazy<LockscreenContent>,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Lockscreen
- private val actionsViewModel: LockscreenSceneActionsViewModel by lazy {
+ private val actionsViewModel: LockscreenUserActionsViewModel by lazy {
actionsViewModelFactory.create()
}
- override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- actionsViewModel.actions
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
override suspend fun onActivated(): Nothing {
actionsViewModel.activate()
@@ -70,7 +69,7 @@
lockscreenContent: Lazy<LockscreenContent>,
modifier: Modifier = Modifier,
) {
- animateSceneFloatAsState(
+ animateContentFloatAsState(
value = QuickSettings.SharedValues.SquishinessValues.LockscreenSceneStarting,
key = QuickSettings.SharedValues.TilesSquishiness,
)
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..2a2c2fc 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
@@ -21,12 +21,16 @@
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
@@ -42,6 +46,7 @@
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.res.R
import java.util.Optional
import javax.inject.Inject
import kotlin.math.roundToInt
@@ -73,9 +78,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) {
@@ -119,7 +122,7 @@
with(notificationSection) {
Notifications(
areNotificationsVisible = areNotificationsVisible,
- isShadeLayoutWide = isShadeLayoutWide,
+ isShadeLayoutWide = true,
burnInParams = null,
modifier =
Modifier.fillMaxWidth(0.5f)
@@ -129,13 +132,27 @@
}
}
}
- if (!isShadeLayoutWide && !isBypassEnabled) {
- with(notificationSection) {
- Notifications(
- areNotificationsVisible = areNotificationsVisible,
- isShadeLayoutWide = isShadeLayoutWide,
- burnInParams = null,
- modifier = Modifier.weight(weight = 1f)
+
+ val aodIconPadding: Dp =
+ dimensionResource(R.dimen.below_clock_padding_start_icons)
+
+ with(notificationSection) {
+ if (!isShadeLayoutWide && !isBypassEnabled) {
+ Box(modifier = Modifier.weight(weight = 1f)) {
+ AodNotificationIcons(
+ modifier =
+ Modifier.align(alignment = Alignment.TopStart)
+ .padding(start = aodIconPadding),
+ )
+ Notifications(
+ areNotificationsVisible = areNotificationsVisible,
+ isShadeLayoutWide = false,
+ burnInParams = null,
+ )
+ }
+ } else {
+ AodNotificationIcons(
+ modifier = Modifier.padding(start = aodIconPadding),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 4129c25..a525f36 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.util.DisplayMetrics
-import android.view.View
import android.view.WindowManager
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -45,9 +44,11 @@
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LongPressHandlingViewLogger
+import com.android.systemui.log.dagger.LongPressTouchLog
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
-import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
@@ -66,7 +67,7 @@
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
private val vibratorHelper: Lazy<VibratorHelper>,
- private val notificationPanelView: NotificationPanelView,
+ @LongPressTouchLog private val logBuffer: LogBuffer,
) {
@Composable
fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
@@ -74,29 +75,30 @@
return
}
- notificationPanelView.findViewById<View?>(R.id.lock_icon_view)?.let {
- notificationPanelView.removeView(it)
- }
-
val context = LocalContext.current
AndroidView(
factory = { context ->
val view =
if (DeviceEntryUdfpsRefactor.isEnabled) {
- DeviceEntryIconView(context, null).apply {
- id = R.id.device_entry_icon_view
- DeviceEntryIconViewBinder.bind(
- applicationScope,
- this,
- deviceEntryIconViewModel.get(),
- deviceEntryForegroundViewModel.get(),
- deviceEntryBackgroundViewModel.get(),
- falsingManager.get(),
- vibratorHelper.get(),
- overrideColor,
+ DeviceEntryIconView(
+ context,
+ null,
+ logger = LongPressHandlingViewLogger(logBuffer, tag = TAG)
)
- }
+ .apply {
+ id = R.id.device_entry_icon_view
+ DeviceEntryIconViewBinder.bind(
+ applicationScope,
+ this,
+ deviceEntryIconViewModel.get(),
+ deviceEntryForegroundViewModel.get(),
+ deviceEntryBackgroundViewModel.get(),
+ falsingManager.get(),
+ vibratorHelper.get(),
+ overrideColor,
+ )
+ }
} else {
// KeyguardBottomAreaRefactor.isEnabled
LockIconView(context, null).apply {
@@ -185,6 +187,10 @@
return IntRect(center, radius)
}
+
+ companion object {
+ private const val TAG = "LockSection"
+ }
}
private val LockIconElementKey = ElementKey("LockIcon")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 18e1092..6fc51e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -17,32 +17,60 @@
package com.android.systemui.keyguard.ui.composable.section
import android.view.ViewGroup
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.thenIf
+import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.MigrateClocksToBlueprint
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
+import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.stopAnimating
+import com.android.systemui.util.ui.value
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.launch
@SysUISingleton
class NotificationSection
@@ -55,6 +83,13 @@
sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
stackScrollLayout: NotificationStackScrollLayout,
sharedNotificationContainerBinder: SharedNotificationContainerBinder,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+ private val configurationState: ConfigurationState,
+ private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
+ private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
+ private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
+ private val systemBarUtilsState: SystemBarUtilsState,
+ private val clockInteractor: KeyguardClockInteractor,
) {
init {
@@ -80,6 +115,47 @@
}
@Composable
+ fun AodNotificationIcons(modifier: Modifier = Modifier) {
+ val isVisible by
+ keyguardRootViewModel.isNotifIconContainerVisible.collectAsStateWithLifecycle()
+ val transitionState = remember { MutableTransitionState(isVisible.value) }
+ LaunchedEffect(key1 = isVisible, key2 = transitionState.isIdle) {
+ transitionState.targetState = isVisible.value
+ if (isVisible.isAnimating && transitionState.isIdle) {
+ isVisible.stopAnimating()
+ }
+ }
+ val burnIn = rememberBurnIn(clockInteractor)
+ AnimatedVisibility(
+ visibleState = transitionState,
+ enter = fadeIn(),
+ exit = fadeOut(),
+ modifier =
+ modifier
+ .height(dimensionResource(R.dimen.notification_shelf_height))
+ .burnInAware(aodBurnInViewModel, burnIn.parameters),
+ ) {
+ val scope = rememberCoroutineScope()
+ AndroidView(
+ factory = { context ->
+ NotificationIconContainer(context, null).also { nic ->
+ scope.launch {
+ NotificationIconContainerViewBinder.bind(
+ nic,
+ nicAodViewModel,
+ configurationState,
+ systemBarUtilsState,
+ iconBindingFailureTracker,
+ nicAodIconViewStore,
+ )
+ }
+ }
+ },
+ )
+ }
+ }
+
+ @Composable
fun SceneScope.HeadsUpNotifications() {
SnoozeableHeadsUpNotificationSpace(
stackScrollView = stackScrollView.get(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 0eeb79b..97d89a2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -93,7 +93,7 @@
// Update state whenever currentSceneKey has changed.
LaunchedEffect(state, currentScene) {
if (currentScene != state.transitionState.currentScene) {
- state.setTargetScene(currentScene, coroutineScope = this)
+ state.setTargetScene(currentScene, animationScope = this)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
index 5dccb68..d523232 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
@@ -22,29 +22,32 @@
import com.android.compose.animation.scene.SceneTransitionLayoutState
import com.android.compose.animation.scene.StaticElementContentPicker
import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.shared.flag.DualShade
/** [ElementContentPicker] implementation for the media carousel object. */
object MediaContentPicker : StaticElementContentPicker {
override val contents =
setOf(
+ Overlays.NotificationsShade,
+ Overlays.QuickSettingsShade,
Scenes.Lockscreen,
Scenes.Shade,
Scenes.QuickSettings,
- Scenes.QuickSettingsShade,
- Scenes.Communal
+ Scenes.Communal,
)
override fun contentDuringTransition(
element: ElementKey,
transition: TransitionState.Transition,
fromContentZIndex: Float,
- toContentZIndex: Float
+ toContentZIndex: Float,
): ContentKey {
return when {
shouldElevateMedia(transition) -> {
- Scenes.Shade
+ if (DualShade.isEnabled) Overlays.NotificationsShade else Scenes.Shade
}
transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.Communal) -> {
Scenes.Lockscreen
@@ -52,6 +55,12 @@
transition.isTransitioningBetween(Scenes.QuickSettings, Scenes.Shade) -> {
Scenes.QuickSettings
}
+ transition.isTransitioningBetween(
+ Overlays.QuickSettingsShade,
+ Overlays.NotificationsShade,
+ ) -> {
+ Overlays.QuickSettingsShade
+ }
transition.toContent in contents -> transition.toContent
else -> {
check(transition.fromContent in contents) {
@@ -65,7 +74,8 @@
/** Returns true when the media should be laid on top of the rest for the given [transition]. */
fun shouldElevateMedia(transition: TransitionState.Transition): Boolean {
- return transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade)
+ return transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade) ||
+ transition.isTransitioningBetween(Scenes.Lockscreen, Overlays.NotificationsShade)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index 4b3a39b..897a861 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -23,11 +23,13 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.IntOffset
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
+import kotlin.math.max
import kotlin.math.roundToInt
+import kotlin.math.tanh
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -36,6 +38,7 @@
coroutineScope: CoroutineScope,
canScrollForward: () -> Boolean
): Modifier {
+ val screenHeight = LocalRawScreenHeight.current
val overscrollOffset = remember { Animatable(0f) }
val stackNestedScrollConnection = remember {
NotificationStackNestedScrollConnection(
@@ -43,7 +46,13 @@
canScrollForward = canScrollForward,
onScroll = { offsetAvailable ->
coroutineScope.launch {
- overscrollOffset.snapTo(overscrollOffset.value + offsetAvailable * 0.3f)
+ val maxProgress = screenHeight * 0.2f
+ val tilt = 3f
+ var offset =
+ overscrollOffset.value +
+ maxProgress * tanh(x = offsetAvailable / (maxProgress * tilt))
+ offset = max(offset, -1f * maxProgress)
+ overscrollOffset.snapTo(offset)
}
},
onStop = { velocityAvailable ->
@@ -79,13 +88,7 @@
offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
},
canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
- canContinueScroll = { source ->
- if (source == NestedScrollSource.SideEffect) {
- stackOffset() > STACK_OVERSCROLL_FLING_MIN_OFFSET
- } else {
- true
- }
- },
+ canContinueScroll = { stackOffset() > 0f },
canScrollOnFling = true,
onStart = { offsetAvailable -> onStart(offsetAvailable) },
onScroll = { offsetAvailable ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index a2beba8..91ecfc1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -667,4 +667,3 @@
private val DEBUG_BOX_COLOR = Color(0f, 1f, 0f, 0.2f)
private const val HUN_SNOOZE_POSITIONAL_THRESHOLD_FRACTION = 0.25f
private const val HUN_SNOOZE_VELOCITY_THRESHOLD = -70f
-internal const val STACK_OVERSCROLL_FLING_MIN_OFFSET = -100f
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
new file mode 100644
index 0000000..a22becc
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.notifications.ui.composable
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
+import com.android.systemui.scene.session.ui.composable.SaveableSession
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.phone.ui.TintedIconManager
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class NotificationsShadeOverlay
+@Inject
+constructor(
+ private val actionsViewModelFactory: NotificationsShadeOverlayActionsViewModel.Factory,
+ private val contentViewModelFactory: NotificationsShadeOverlayContentViewModel.Factory,
+ private val tintedIconManagerFactory: TintedIconManager.Factory,
+ private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
+ private val statusBarIconController: StatusBarIconController,
+ private val shadeSession: SaveableSession,
+ private val stackScrollView: Lazy<NotificationScrollView>,
+) : Overlay {
+
+ override val key = Overlays.NotificationsShade
+
+ private val actionsViewModel: NotificationsShadeOverlayActionsViewModel by lazy {
+ actionsViewModelFactory.create()
+ }
+
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
+
+ override suspend fun activate(): Nothing {
+ actionsViewModel.activate()
+ }
+
+ @Composable
+ override fun ContentScope.Content(
+ modifier: Modifier,
+ ) {
+ val viewModel =
+ rememberViewModel("NotificationsShadeOverlay-viewModel") {
+ contentViewModelFactory.create()
+ }
+ val placeholderViewModel =
+ rememberViewModel("NotificationsShadeOverlay-notifPlaceholderViewModel") {
+ viewModel.notificationsPlaceholderViewModelFactory.create()
+ }
+
+ OverlayShade(
+ modifier = modifier,
+ onScrimClicked = viewModel::onScrimClicked,
+ ) {
+ Column {
+ ExpandedShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ modifier = Modifier.padding(horizontal = 16.dp),
+ )
+
+ NotificationScrollingStack(
+ shadeSession = shadeSession,
+ stackScrollView = stackScrollView.get(),
+ viewModel = placeholderViewModel,
+ maxScrimTop = { 0f },
+ shouldPunchHoleBehindScrim = false,
+ shouldFillMaxSize = false,
+ shouldReserveSpaceForNavBar = false,
+ shadeMode = ShadeMode.Dual,
+ modifier = Modifier.fillMaxWidth(),
+ )
+
+ // Communicates the bottom position of the drawable area within the shade to NSSL.
+ NotificationStackCutoffGuideline(
+ stackScrollView = stackScrollView.get(),
+ viewModel = placeholderViewModel,
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
index e9c96ea..1f4cd04 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
@@ -27,24 +27,21 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeUserActionsViewModel
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import dagger.Lazy
-import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -52,8 +49,7 @@
class NotificationsShadeScene
@Inject
constructor(
- private val actionsViewModelFactory: NotificationsShadeSceneActionsViewModel.Factory,
- private val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
+ private val actionsViewModelFactory: NotificationsShadeUserActionsViewModel.Factory,
private val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
@@ -61,17 +57,15 @@
private val statusBarIconController: StatusBarIconController,
private val shadeSession: SaveableSession,
private val stackScrollView: Lazy<NotificationScrollView>,
- private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.NotificationsShade
- private val actionsViewModel: NotificationsShadeSceneActionsViewModel by lazy {
+ private val actionsViewModel: NotificationsShadeUserActionsViewModel by lazy {
actionsViewModelFactory.create()
}
- override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- actionsViewModel.actions
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
override suspend fun onActivated(): Nothing {
actionsViewModel.activate()
@@ -88,8 +82,7 @@
OverlayShade(
modifier = modifier,
- viewModelFactory = overlayShadeViewModelFactory,
- lockscreenContent = lockscreenContent,
+ onScrimClicked = {},
) {
Column {
ExpandedShadeHeader(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index d372577..d34295e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -93,12 +93,12 @@
import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS
-import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneActionsViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneContentViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsUserActionsViewModel
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
@@ -113,9 +113,11 @@
import javax.inject.Inject
import javax.inject.Named
import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
/** The Quick Settings (AKA "QS") scene shows the quick setting tiles. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class QuickSettingsScene
@Inject
@@ -123,22 +125,21 @@
private val shadeSession: SaveableSession,
private val notificationStackScrollView: Lazy<NotificationScrollView>,
private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
- private val actionsViewModelFactory: QuickSettingsSceneActionsViewModel.Factory,
+ private val actionsViewModelFactory: QuickSettingsUserActionsViewModel.Factory,
private val contentViewModelFactory: QuickSettingsSceneContentViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
private val mediaCarouselController: MediaCarouselController,
@Named(MediaModule.QS_PANEL) private val mediaHost: MediaHost,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.QuickSettings
- private val actionsViewModel: QuickSettingsSceneActionsViewModel by lazy {
+ private val actionsViewModel: QuickSettingsUserActionsViewModel by lazy {
actionsViewModelFactory.create()
}
- override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- actionsViewModel.actions
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
override suspend fun onActivated(): Nothing {
actionsViewModel.activate()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
new file mode 100644
index 0000000..f8d0588
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -0,0 +1,165 @@
+/*
+ * 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.qs.ui.composable
+
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.panels.ui.compose.EditMode
+import com.android.systemui.qs.panels.ui.compose.TileGrid
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayContentViewModel
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.phone.ui.TintedIconManager
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class QuickSettingsShadeOverlay
+@Inject
+constructor(
+ private val actionsViewModelFactory: QuickSettingsShadeOverlayActionsViewModel.Factory,
+ private val contentViewModelFactory: QuickSettingsShadeOverlayContentViewModel.Factory,
+ private val tintedIconManagerFactory: TintedIconManager.Factory,
+ private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
+ private val statusBarIconController: StatusBarIconController,
+) : Overlay {
+
+ override val key = Overlays.QuickSettingsShade
+
+ private val actionsViewModel: QuickSettingsShadeOverlayActionsViewModel by lazy {
+ actionsViewModelFactory.create()
+ }
+
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
+
+ override suspend fun activate(): Nothing {
+ actionsViewModel.activate()
+ }
+
+ @Composable
+ override fun ContentScope.Content(
+ modifier: Modifier,
+ ) {
+ val viewModel =
+ rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() }
+
+ OverlayShade(
+ modifier = modifier,
+ onScrimClicked = viewModel::onScrimClicked,
+ ) {
+ Column {
+ ExpandedShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ modifier = Modifier.padding(QuickSettingsShade.Dimensions.Padding),
+ )
+
+ ShadeBody(
+ viewModel = viewModel.quickSettingsContainerViewModel,
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun ShadeBody(
+ viewModel: QuickSettingsContainerViewModel,
+) {
+ val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
+
+ AnimatedContent(
+ targetState = isEditing,
+ transitionSpec = { fadeIn(tween(500)) togetherWith fadeOut(tween(500)) }
+ ) { editing ->
+ if (editing) {
+ EditMode(
+ viewModel = viewModel.editModeViewModel,
+ modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
+ )
+ } else {
+ QuickSettingsLayout(
+ viewModel = viewModel,
+ modifier = Modifier.sysuiResTag("quick_settings_panel")
+ )
+ }
+ }
+}
+
+@Composable
+private fun QuickSettingsLayout(
+ viewModel: QuickSettingsContainerViewModel,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
+ ) {
+ BrightnessSliderContainer(
+ viewModel = viewModel.brightnessSliderViewModel,
+ modifier =
+ Modifier.fillMaxWidth()
+ .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
+ )
+ TileGrid(
+ viewModel = viewModel.tileGridViewModel,
+ modifier =
+ Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
+ viewModel.editModeViewModel::startEditing,
+ )
+ }
+}
+
+object QuickSettingsShade {
+
+ object Dimensions {
+ val Padding = 16.dp
+ val BrightnessSliderHeight = 64.dp
+ val GridMaxHeight = 800.dp
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index 90d7da6..e27c7e2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -16,51 +16,26 @@
package com.android.systemui.qs.ui.composable
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.core.tween
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.togetherWith
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
-import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
-import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.qs.panels.ui.compose.EditMode
-import com.android.systemui.qs.panels.ui.compose.TileGrid
-import com.android.systemui.qs.ui.composable.QuickSettingsShade.Transitions.QuickSettingsLayoutEnter
-import com.android.systemui.qs.ui.composable.QuickSettingsShade.Transitions.QuickSettingsLayoutExit
-import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
-import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneActionsViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneContentViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeUserActionsViewModel
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
-import dagger.Lazy
-import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -68,23 +43,21 @@
class QuickSettingsShadeScene
@Inject
constructor(
- private val actionsViewModelFactory: QuickSettingsShadeSceneActionsViewModel.Factory,
+ private val actionsViewModelFactory: QuickSettingsShadeUserActionsViewModel.Factory,
private val contentViewModelFactory: QuickSettingsShadeSceneContentViewModel.Factory,
- private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
private val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.QuickSettingsShade
- private val actionsViewModel: QuickSettingsShadeSceneActionsViewModel by lazy {
+ private val actionsViewModel: QuickSettingsShadeUserActionsViewModel by lazy {
actionsViewModelFactory.create()
}
- override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- actionsViewModel.actions
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
override suspend fun onActivated(): Nothing {
actionsViewModel.activate()
@@ -96,10 +69,10 @@
) {
val viewModel =
rememberViewModel("QuickSettingsShadeScene") { contentViewModelFactory.create() }
+
OverlayShade(
- viewModelFactory = viewModel.overlayShadeViewModelFactory,
- lockscreenContent = lockscreenContent,
modifier = modifier,
+ onScrimClicked = {},
) {
Column {
ExpandedShadeHeader(
@@ -117,68 +90,3 @@
}
}
}
-
-@Composable
-fun ShadeBody(
- viewModel: QuickSettingsContainerViewModel,
-) {
- val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
-
- AnimatedContent(
- targetState = isEditing,
- transitionSpec = { QuickSettingsLayoutEnter togetherWith QuickSettingsLayoutExit }
- ) { editing ->
- if (editing) {
- EditMode(
- viewModel = viewModel.editModeViewModel,
- modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
- )
- } else {
- QuickSettingsLayout(
- viewModel = viewModel,
- modifier = Modifier.sysuiResTag("quick_settings_panel")
- )
- }
- }
-}
-
-@Composable
-private fun QuickSettingsLayout(
- viewModel: QuickSettingsContainerViewModel,
- modifier: Modifier = Modifier,
-) {
- Column(
- verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
- ) {
- BrightnessSliderContainer(
- viewModel = viewModel.brightnessSliderViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
- )
- TileGrid(
- viewModel = viewModel.tileGridViewModel,
- modifier =
- Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
- viewModel.editModeViewModel::startEditing,
- )
- }
-}
-
-object QuickSettingsShade {
-
- object Dimensions {
- val Padding = 16.dp
- val BrightnessSliderHeight = 64.dp
- val GridMaxHeight = 800.dp
- }
-
- object Transitions {
- val QuickSettingsLayoutEnter: EnterTransition = fadeIn(tween(500))
- val QuickSettingsLayoutExit: ExitTransition = fadeOut(tween(500))
- val QuickSettingsEditorEnter: EnterTransition = fadeIn(tween(500))
- val QuickSettingsEditorExit: ExitTransition = fadeOut(tween(500))
- }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ActionableContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ActionableContent.kt
new file mode 100644
index 0000000..8fe6893
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ActionableContent.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.scene.ui.composable
+
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface for content that can respond to user-actions. */
+interface ActionableContent {
+ /**
+ * The mapping between [UserAction] and destination [UserActionResult]s.
+ *
+ * When the scene framework detects a user action, if the current scene has a map entry for that
+ * user action, the framework starts a transition to the content specified in the map.
+ *
+ * Once the content is shown, the scene framework will read this property and set up a collector
+ * to watch for new mapping values. For each map entry, the scene framework will set up user
+ * input handling for its [UserAction] and, if such a user action is detected, initiate a
+ * transition to the specified [UserActionResult].
+ *
+ * Note that reading from this method does _not_ mean that any user action has occurred.
+ * Instead, the property is read before any user action/gesture is detected so that the
+ * framework can decide whether to set up gesture/input detectors/listeners in case user actions
+ * of the given types ever occur.
+ *
+ * A missing value for a specific [UserAction] means that the user action of the given type is
+ * not currently active in the top-most content (in z-index order) and should be ignored by the
+ * framework until the top-most content changes.
+ */
+ val userActions: Flow<Map<UserAction, UserActionResult>>
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
deleted file mode 100644
index 3da6a02..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2023 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.scene.ui.composable
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.scene.shared.model.Scene
-
-/** Compose-capable extension of [Scene]. */
-interface ComposableScene : Scene {
- @Composable fun SceneScope.Content(modifier: Modifier)
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index cbbace4..ae5dd8a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -23,8 +23,8 @@
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.animateSceneDpAsState
-import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.animateContentDpAsState
+import com.android.compose.animation.scene.animateContentFloatAsState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
@@ -33,7 +33,7 @@
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.Default
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.viewmodel.GoneSceneActionsViewModel
+import com.android.systemui.scene.ui.viewmodel.GoneUserActionsViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import dagger.Lazy
@@ -50,14 +50,13 @@
constructor(
private val notificationStackScrolLView: Lazy<NotificationScrollView>,
private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
- private val viewModelFactory: GoneSceneActionsViewModel.Factory,
-) : ExclusiveActivatable(), ComposableScene {
+ private val viewModelFactory: GoneUserActionsViewModel.Factory,
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Gone
- private val actionsViewModel: GoneSceneActionsViewModel by lazy { viewModelFactory.create() }
+ private val actionsViewModel: GoneUserActionsViewModel by lazy { viewModelFactory.create() }
- override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- actionsViewModel.actions
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
override suspend fun onActivated(): Nothing {
actionsViewModel.activate()
@@ -67,11 +66,11 @@
override fun SceneScope.Content(
modifier: Modifier,
) {
- animateSceneFloatAsState(
+ animateContentFloatAsState(
value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting,
key = QuickSettings.SharedValues.TilesSquishiness,
)
- animateSceneDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false)
+ animateContentDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false)
Spacer(modifier.fillMaxSize())
SnoozeableHeadsUpNotificationSpace(
stackScrollView = notificationStackScrolLView.get(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.kt
index d62befd..609ce90 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.kt
@@ -18,9 +18,14 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.lifecycle.Activatable
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
/**
* Defines interface for classes that can describe an "overlay".
@@ -29,9 +34,17 @@
* container takes care of rendering any current overlays and allowing overlays to be shown, hidden,
* or replaced based on a user action.
*/
-interface Overlay : Activatable {
+interface Overlay : Activatable, ActionableContent {
/** Uniquely-identifying key for this overlay. The key must be unique within its container. */
val key: OverlayKey
+ /**
+ * The user actions supported by this overlay.
+ *
+ * @see [ActionableContent.userActions]
+ */
+ override val userActions: Flow<Map<UserAction, UserActionResult>>
+ get() = flowOf(mapOf(Back to UserActionResult.HideOverlay(key)))
+
@Composable fun ContentScope.Content(modifier: Modifier)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
new file mode 100644
index 0000000..8d8ab8e
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.scene.ui.composable
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.lifecycle.Activatable
+
+/**
+ * Defines interface for classes that can describe a "scene".
+ *
+ * In the scene framework, there can be multiple scenes in a single scene "container". The container
+ * takes care of rendering the current scene and allowing scenes to be switched from one to another
+ * based on either user action (for example, swiping down while on the lock screen scene may switch
+ * to the shade scene).
+ */
+interface Scene : Activatable, ActionableContent {
+
+ /** Uniquely-identifying key for this scene. The key must be unique within its container. */
+ val key: SceneKey
+
+ @Composable fun SceneScope.Content(modifier: Modifier)
+}
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 f9723d9..df101c5 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
@@ -54,11 +54,11 @@
* containers.
*
* @param viewModel The UI state holder for this container.
- * @param sceneByKey Mapping of [ComposableScene] by [SceneKey], ordered by z-order such that the
- * last scene is rendered on top of all other scenes. It's critical that this map contains exactly
- * and only the scenes on this container. In other words: (a) there should be no scene in this map
- * that is not in the configuration for this container and (b) all scenes in the configuration
- * must have entries in this map.
+ * @param sceneByKey Mapping of [Scene] by [SceneKey], ordered by z-order such that the last scene
+ * is rendered on top of all other scenes. It's critical that this map contains exactly and only
+ * the scenes on this container. In other words: (a) there should be no scene in this map that is
+ * not in the configuration for this container and (b) all scenes in the configuration must have
+ * entries in this map.
* @param overlayByKey Mapping of [Overlay] by [OverlayKey], ordered by z-order such that the last
* overlay is rendered on top of all other overlays. It's critical that this map contains exactly
* and only the overlays on this container. In other words: (a) there should be no overlay in this
@@ -69,7 +69,7 @@
@Composable
fun SceneContainer(
viewModel: SceneContainerViewModel,
- sceneByKey: Map<SceneKey, ComposableScene>,
+ sceneByKey: Map<SceneKey, Scene>,
overlayByKey: Map<OverlayKey, Overlay>,
initialSceneKey: SceneKey,
dataSourceDelegator: SceneDataSourceDelegator,
@@ -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]?.destinationScenes?.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()
}
}
@@ -122,30 +128,37 @@
}
},
) {
- SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) {
- sceneByKey.forEach { (sceneKey, composableScene) ->
+ SceneTransitionLayout(
+ state = state,
+ modifier = modifier.fillMaxSize(),
+ swipeSourceDetector = viewModel.edgeDetector,
+ ) {
+ sceneByKey.forEach { (sceneKey, scene) ->
scene(
key = sceneKey,
userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap())
) {
// Activate the scene.
- LaunchedEffect(composableScene) { composableScene.activate() }
+ LaunchedEffect(scene) { scene.activate() }
// Render the scene.
- with(composableScene) {
+ with(scene) {
[email protected](
modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(),
)
}
}
}
- 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) { [email protected](Modifier) }
+ with(overlay) { [email protected](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 39fc7ef..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
@@ -1,16 +1,16 @@
package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.gestures.Orientation
-import com.android.compose.animation.scene.Edge
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
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
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
@@ -48,18 +48,8 @@
// Scene transitions
from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() }
- from(Scenes.Gone, to = Scenes.NotificationsShade) {
- goneToNotificationsShadeTransition(Edge.Top)
- }
- from(Scenes.Gone, to = Scenes.NotificationsShade, key = OpenBottomShade) {
- goneToNotificationsShadeTransition(Edge.Bottom)
- }
- from(Scenes.Gone, to = Scenes.QuickSettingsShade) {
- goneToQuickSettingsShadeTransition(Edge.Top)
- }
- from(Scenes.Gone, to = Scenes.QuickSettingsShade, key = OpenBottomShade) {
- goneToQuickSettingsShadeTransition(Edge.Bottom)
- }
+ from(Scenes.Gone, to = Scenes.NotificationsShade) { goneToNotificationsShadeTransition() }
+ from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade, key = ToSplitShade) { goneToSplitShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
@@ -71,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/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
index e12a8bd..6738b97 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
@@ -69,7 +69,7 @@
state.setTargetScene(
targetScene = toScene,
transitionKey = transitionKey,
- coroutineScope = coroutineScope,
+ animationScope = coroutineScope,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
index fb41374..48ec198 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
@@ -16,12 +16,10 @@
package com.android.systemui.scene.ui.composable.transitions
-import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
fun TransitionBuilder.goneToNotificationsShadeTransition(
- edge: Edge = Edge.Top,
durationScale: Double = 1.0,
) {
- toNotificationsShadeTransition(edge, durationScale)
+ toNotificationsShadeTransition(durationScale)
}
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/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
index 5401936..826a255 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
@@ -19,14 +19,19 @@
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.communal.ui.compose.AllElements
+import com.android.systemui.communal.ui.compose.Communal
import com.android.systemui.scene.shared.model.Scenes
fun TransitionBuilder.lockscreenToCommunalTransition() {
- spec = tween(durationMillis = 500)
+ spec = tween(durationMillis = 1000)
- // Translate lockscreen to the left.
+ // Translate lockscreen to the start direction.
translate(Scenes.Lockscreen.rootElementKey, Edge.Start)
- // Translate communal from the right.
- translate(Scenes.Communal.rootElementKey, Edge.End)
+ // Translate communal hub grid from the end direction.
+ translate(Communal.Elements.Grid, Edge.End)
+
+ // Fade all communal hub elements.
+ timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 05949b2..337f53a5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -19,12 +19,9 @@
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.Shade
@@ -32,11 +29,6 @@
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toNotificationsShadeTransition(
- /**
- * The edge where the shade will animate from. This is statically determined (i.e. doesn't
- * change during runtime).
- */
- edge: Edge = Edge.Top,
durationScale: Double = 1.0,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
@@ -45,17 +37,11 @@
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
- distance =
- object : UserActionDistance {
- override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
- orientation: Orientation,
- ): Float {
- return fromSceneSize.height.toFloat() * 2 / 3f
- }
- }
+ distance = UserActionDistance { fromSceneSize, orientation ->
+ fromSceneSize.height.toFloat() * 2 / 3f
+ }
- translate(OverlayShade.Elements.Panel, edge)
+ translate(OverlayShade.Elements.Panel, Edge.Top)
fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 595bbb0..8922224 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -40,56 +40,30 @@
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
-import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.shared.model.ShadeAlignment
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
-import com.android.systemui.util.kotlin.getOrNull
-import dagger.Lazy
-import java.util.Optional
-/** The overlay shade renders a lightweight shade UI container on top of a background scene. */
+/** Renders a lightweight shade UI container, as an overlay. */
@Composable
fun SceneScope.OverlayShade(
- viewModelFactory: OverlayShadeViewModel.Factory,
- lockscreenContent: Lazy<Optional<LockscreenContent>>,
+ onScrimClicked: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
- val viewModel = rememberViewModel("OverlayShade") { viewModelFactory.create() }
- val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()
-
Box(modifier) {
- if (backgroundScene == Scenes.Lockscreen) {
- // Lockscreen content is optionally injected, because variants of System UI without a
- // lockscreen cannot provide it.
- val lockscreenContentOrNull = lockscreenContent.get().getOrNull()
- lockscreenContentOrNull?.apply { Content(Modifier.fillMaxSize()) }
- }
-
- Scrim(onClicked = viewModel::onScrimClicked)
+ Scrim(onClicked = onScrimClicked)
Box(
modifier = Modifier.fillMaxSize().panelPadding(),
- contentAlignment =
- if (viewModel.panelAlignment == ShadeAlignment.Top) {
- Alignment.TopEnd
- } else {
- Alignment.BottomEnd
- },
+ contentAlignment = Alignment.TopEnd,
) {
Panel(
modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(),
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 b7c6edc..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
@@ -30,7 +30,7 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
-import androidx.compose.foundation.layout.displayCutoutPadding
+import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -47,8 +47,9 @@
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.CompositingStrategy
@@ -99,17 +100,16 @@
import com.android.systemui.notifications.ui.composable.NotificationStackCutoffGuideline
import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
import com.android.systemui.qs.ui.composable.BrightnessMirror
-import com.android.systemui.qs.ui.composable.QSMediaMeasurePolicy
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQQS
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.shade.ui.viewmodel.ShadeSceneActionsViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneContentViewModel
+import com.android.systemui.shade.ui.viewmodel.ShadeUserActionsViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.phone.StatusBarLocation
@@ -120,6 +120,7 @@
import javax.inject.Inject
import javax.inject.Named
import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
object Shade {
@@ -146,13 +147,14 @@
}
/** The shade scene shows scrolling list of notifications and some of the quick setting tiles. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class ShadeScene
@Inject
constructor(
private val shadeSession: SaveableSession,
private val notificationStackScrollView: Lazy<NotificationScrollView>,
- private val actionsViewModelFactory: ShadeSceneActionsViewModel.Factory,
+ private val actionsViewModelFactory: ShadeUserActionsViewModel.Factory,
private val contentViewModelFactory: ShadeSceneContentViewModel.Factory,
private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
@@ -161,11 +163,11 @@
private val mediaCarouselController: MediaCarouselController,
@Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
@Named(QS_PANEL) private val qsMediaHost: MediaHost,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Shade
- private val actionsViewModel: ShadeSceneActionsViewModel by lazy {
+ private val actionsViewModel: ShadeUserActionsViewModel by lazy {
actionsViewModelFactory.create()
}
@@ -173,8 +175,7 @@
actionsViewModel.activate()
}
- override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- actionsViewModel.actions
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
@Composable
override fun SceneScope.Content(
@@ -269,13 +270,14 @@
shadeSession: SaveableSession,
) {
val cutoutLocation = LocalDisplayCutout.current.location
+ val cutoutInsets = WindowInsets.Companion.displayCutout
val isLandscape = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
val usingCollapsedLandscapeMedia =
Utils.useCollapsedMediaInLandscape(LocalContext.current.resources)
val isExpanded = !usingCollapsedLandscapeMedia || !isLandscape
mediaHost.expansion = if (isExpanded) EXPANDED else COLLAPSED
- val maxNotifScrimTop = remember { mutableStateOf(0f) }
+ var maxNotifScrimTop by remember { mutableIntStateOf(0) }
val tileSquishiness by
animateSceneFloatAsState(
value = 1f,
@@ -301,6 +303,26 @@
viewModel.qsSceneAdapter,
)
}
+ val shadeHorizontalPadding =
+ dimensionResource(id = R.dimen.notification_panel_margin_horizontal)
+ val shadeMeasurePolicy =
+ remember(mediaInRow) {
+ SingleShadeMeasurePolicy(
+ isMediaInRow = mediaInRow,
+ mediaOffset = { mediaOffset.roundToPx() },
+ onNotificationsTopChanged = { maxNotifScrimTop = it },
+ mediaZIndex = {
+ if (MediaContentPicker.shouldElevateMedia(layoutState)) 1f else 0f
+ },
+ cutoutInsetsProvider = {
+ if (cutoutLocation == CutoutLocation.CENTER) {
+ null
+ } else {
+ cutoutInsets
+ }
+ }
+ )
+ }
Box(
modifier =
@@ -318,101 +340,57 @@
.background(colorResource(R.color.shade_scrim_background_dark)),
)
Layout(
- contents =
- listOf(
- {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier =
- Modifier.fillMaxWidth()
- .thenIf(isEmptySpaceClickable) {
- Modifier.clickable(
- onClick = { viewModel.onEmptySpaceClicked() }
- )
- }
- .thenIf(cutoutLocation != CutoutLocation.CENTER) {
- Modifier.displayCutoutPadding()
- },
- ) {
- CollapsedShadeHeader(
- viewModelFactory = viewModel.shadeHeaderViewModelFactory,
- createTintedIconManager = createTintedIconManager,
- createBatteryMeterViewController = createBatteryMeterViewController,
- statusBarIconController = statusBarIconController,
- )
-
- val content: @Composable () -> Unit = {
- Box(
- Modifier.element(QuickSettings.Elements.QuickQuickSettings)
- .layoutId(QSMediaMeasurePolicy.LayoutId.QS)
- ) {
- QuickSettings(
- viewModel.qsSceneAdapter,
- { viewModel.qsSceneAdapter.qqsHeight },
- isSplitShade = false,
- squishiness = { tileSquishiness },
- )
- }
-
- ShadeMediaCarousel(
- isVisible = isMediaVisible,
- mediaHost = mediaHost,
- mediaOffsetProvider = mediaOffsetProvider,
- modifier =
- Modifier.layoutId(QSMediaMeasurePolicy.LayoutId.Media),
- carouselController = mediaCarouselController,
- )
- }
- val landscapeQsMediaMeasurePolicy = remember {
- QSMediaMeasurePolicy(
- { viewModel.qsSceneAdapter.qqsHeight },
- { mediaOffset.roundToPx() },
- )
- }
- if (mediaInRow) {
- Layout(
- content = content,
- measurePolicy = landscapeQsMediaMeasurePolicy,
- )
- } else {
- content()
- }
- }
- },
- {
- NotificationScrollingStack(
- shadeSession = shadeSession,
- stackScrollView = notificationStackScrollView,
- viewModel = notificationsPlaceholderViewModel,
- maxScrimTop = { maxNotifScrimTop.value },
- shadeMode = ShadeMode.Single,
- shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
- onEmptySpaceClick =
- viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
- )
- },
+ modifier =
+ Modifier.thenIf(isEmptySpaceClickable) {
+ Modifier.clickable { viewModel.onEmptySpaceClicked() }
+ },
+ content = {
+ CollapsedShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = createTintedIconManager,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ statusBarIconController = statusBarIconController,
+ modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
)
- ) { measurables, constraints ->
- check(measurables.size == 2)
- check(measurables[0].size == 1)
- check(measurables[1].size == 1)
- val quickSettingsPlaceable = measurables[0][0].measure(constraints)
- val notificationsPlaceable = measurables[1][0].measure(constraints)
+ Box(
+ Modifier.element(QuickSettings.Elements.QuickQuickSettings)
+ .layoutId(SingleShadeMeasurePolicy.LayoutId.QuickSettings)
+ .padding(horizontal = shadeHorizontalPadding)
+ ) {
+ QuickSettings(
+ viewModel.qsSceneAdapter,
+ { viewModel.qsSceneAdapter.qqsHeight },
+ isSplitShade = false,
+ squishiness = { tileSquishiness },
+ )
+ }
- maxNotifScrimTop.value = quickSettingsPlaceable.height.toFloat()
+ ShadeMediaCarousel(
+ isVisible = isMediaVisible,
+ isInRow = mediaInRow,
+ mediaHost = mediaHost,
+ mediaOffsetProvider = mediaOffsetProvider,
+ carouselController = mediaCarouselController,
+ modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Media),
+ )
- layout(constraints.maxWidth, constraints.maxHeight) {
- val qsZIndex =
- if (MediaContentPicker.shouldElevateMedia(layoutState)) {
- 1f
- } else {
- 0f
- }
- quickSettingsPlaceable.placeRelative(x = 0, y = 0, zIndex = qsZIndex)
- notificationsPlaceable.placeRelative(x = 0, y = maxNotifScrimTop.value.roundToInt())
- }
- }
+ NotificationScrollingStack(
+ shadeSession = shadeSession,
+ stackScrollView = notificationStackScrollView,
+ viewModel = notificationsPlaceholderViewModel,
+ maxScrimTop = { maxNotifScrimTop.toFloat() },
+ shadeMode = ShadeMode.Single,
+ shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
+ onEmptySpaceClick =
+ viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
+ modifier =
+ Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Notifications)
+ .padding(horizontal = shadeHorizontalPadding),
+ )
+ },
+ measurePolicy = shadeMeasurePolicy,
+ )
Box(
modifier =
Modifier.align(Alignment.BottomCenter)
@@ -600,6 +578,7 @@
ShadeMediaCarousel(
isVisible = isMediaVisible,
+ isInRow = false,
mediaHost = mediaHost,
mediaOffsetProvider = mediaOffsetProvider,
modifier =
@@ -657,6 +636,7 @@
@Composable
private fun SceneScope.ShadeMediaCarousel(
isVisible: Boolean,
+ isInRow: Boolean,
mediaHost: MediaHost,
carouselController: MediaCarouselController,
mediaOffsetProvider: ShadeMediaOffsetProvider,
@@ -668,7 +648,7 @@
mediaHost = mediaHost,
carouselController = carouselController,
offsetProvider =
- if (MediaContentPicker.shouldElevateMedia(layoutState)) {
+ if (isInRow || MediaContentPicker.shouldElevateMedia(layoutState)) {
null
} else {
{ mediaOffsetProvider.offset }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt
new file mode 100644
index 0000000..6275ac3
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.shade.ui.composable
+
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.offset
+import androidx.compose.ui.util.fastFirst
+import androidx.compose.ui.util.fastFirstOrNull
+import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy.LayoutId
+import kotlin.math.max
+
+/**
+ * Lays out elements from the [LayoutId] in the shade. This policy supports the case when the QS and
+ * UMO share the same row and when they should be one below another.
+ */
+class SingleShadeMeasurePolicy(
+ private val isMediaInRow: Boolean,
+ private val mediaOffset: MeasureScope.() -> Int,
+ private val onNotificationsTopChanged: (Int) -> Unit,
+ private val mediaZIndex: () -> Float,
+ private val cutoutInsetsProvider: () -> WindowInsets?,
+) : MeasurePolicy {
+
+ enum class LayoutId {
+ QuickSettings,
+ Media,
+ Notifications,
+ ShadeHeader,
+ }
+
+ override fun MeasureScope.measure(
+ measurables: List<Measurable>,
+ constraints: Constraints,
+ ): MeasureResult {
+ val cutoutInsets: WindowInsets? = cutoutInsetsProvider()
+ val constraintsWithCutout = applyCutout(constraints, cutoutInsets)
+ val insetsLeft = cutoutInsets?.getLeft(this, layoutDirection) ?: 0
+ val insetsTop = cutoutInsets?.getTop(this) ?: 0
+
+ val shadeHeaderPlaceable =
+ measurables
+ .fastFirst { it.layoutId == LayoutId.ShadeHeader }
+ .measure(constraintsWithCutout)
+ val mediaPlaceable =
+ measurables
+ .fastFirstOrNull { it.layoutId == LayoutId.Media }
+ ?.measure(applyMediaConstraints(constraintsWithCutout, isMediaInRow))
+ val quickSettingsPlaceable =
+ measurables
+ .fastFirst { it.layoutId == LayoutId.QuickSettings }
+ .measure(constraintsWithCutout)
+ val notificationsPlaceable =
+ measurables.fastFirst { it.layoutId == LayoutId.Notifications }.measure(constraints)
+
+ val notificationsTop =
+ calculateNotificationsTop(
+ statusBarHeaderPlaceable = shadeHeaderPlaceable,
+ quickSettingsPlaceable = quickSettingsPlaceable,
+ mediaPlaceable = mediaPlaceable,
+ insetsTop = insetsTop,
+ isMediaInRow = isMediaInRow,
+ )
+ onNotificationsTopChanged(notificationsTop)
+
+ return layout(constraints.maxWidth, constraints.maxHeight) {
+ shadeHeaderPlaceable.placeRelative(x = insetsLeft, y = insetsTop)
+ quickSettingsPlaceable.placeRelative(
+ x = insetsLeft,
+ y = insetsTop + shadeHeaderPlaceable.height,
+ )
+
+ if (isMediaInRow) {
+ mediaPlaceable?.placeRelative(
+ x = insetsLeft + constraintsWithCutout.maxWidth / 2,
+ y = mediaOffset() + insetsTop + shadeHeaderPlaceable.height,
+ zIndex = mediaZIndex(),
+ )
+ } else {
+ mediaPlaceable?.placeRelative(
+ x = insetsLeft,
+ y = insetsTop + shadeHeaderPlaceable.height + quickSettingsPlaceable.height,
+ zIndex = mediaZIndex(),
+ )
+ }
+
+ // Notifications don't need to accommodate for horizontal insets
+ notificationsPlaceable.placeRelative(x = 0, y = notificationsTop)
+ }
+ }
+
+ private fun calculateNotificationsTop(
+ statusBarHeaderPlaceable: Placeable,
+ quickSettingsPlaceable: Placeable,
+ mediaPlaceable: Placeable?,
+ insetsTop: Int,
+ isMediaInRow: Boolean,
+ ): Int {
+ val mediaHeight = mediaPlaceable?.height ?: 0
+ return insetsTop +
+ statusBarHeaderPlaceable.height +
+ if (isMediaInRow) {
+ max(quickSettingsPlaceable.height, mediaHeight)
+ } else {
+ quickSettingsPlaceable.height + mediaHeight
+ }
+ }
+
+ private fun applyMediaConstraints(
+ constraints: Constraints,
+ isMediaInRow: Boolean,
+ ): Constraints {
+ return if (isMediaInRow) {
+ constraints.copy(maxWidth = constraints.maxWidth / 2)
+ } else {
+ constraints
+ }
+ }
+
+ private fun MeasureScope.applyCutout(
+ constraints: Constraints,
+ cutoutInsets: WindowInsets?,
+ ): Constraints {
+ return if (cutoutInsets == null) {
+ constraints
+ } else {
+ val left = cutoutInsets.getLeft(this, layoutDirection)
+ val top = cutoutInsets.getTop(this)
+ val right = cutoutInsets.getRight(this, layoutDirection)
+ val bottom = cutoutInsets.getBottom(this)
+
+ constraints.offset(horizontal = -(left + right), vertical = -(top + bottom))
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
index 1db96cf..c9b8013 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -92,7 +92,12 @@
true
}
},
- color = MaterialTheme.colorScheme.surface,
+ color =
+ if (enabled) {
+ MaterialTheme.colorScheme.surface
+ } else {
+ MaterialTheme.colorScheme.surfaceContainerHighest
+ },
shape = RoundedCornerShape(28.dp),
onClick =
if (enabled) {
@@ -119,7 +124,7 @@
modifier = Modifier.basicMarquee(),
text = connectedDeviceViewModel.label.toString(),
style = MaterialTheme.typography.labelMedium,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
+ color = connectedDeviceViewModel.labelColor.toColor(),
maxLines = 1,
)
connectedDeviceViewModel.deviceName?.let {
@@ -127,7 +132,7 @@
modifier = Modifier.basicMarquee(),
text = it.toString(),
style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface,
+ color = connectedDeviceViewModel.deviceNameColor.toColor(),
maxLines = 1,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index 072e91a..d4f3b5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -23,7 +23,6 @@
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
-import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -78,7 +77,6 @@
modifier: Modifier = Modifier,
) {
require(viewModels.isNotEmpty())
- val transition = updateTransition(isExpanded, label = "CollapsableSliders")
Column(modifier = modifier) {
Box(
modifier = Modifier.fillMaxWidth(),
@@ -106,8 +104,9 @@
sliderColors = sliderColors,
)
}
- transition.AnimatedVisibility(
- visible = { it || !isExpandable },
+ AnimatedVisibility(
+ visible = isExpanded || !isExpandable,
+ label = "CollapsableSliders",
enter =
expandVertically(animationSpec = tween(durationMillis = EXPAND_DURATION_MILLIS)),
exit =
@@ -120,23 +119,31 @@
for (index in 1..viewModels.lastIndex) {
val sliderViewModel: SliderViewModel = viewModels[index]
val sliderState by sliderViewModel.slider.collectAsStateWithLifecycle()
- transition.AnimatedVisibility(
- modifier = Modifier.padding(top = 16.dp),
- visible = { it || !isExpandable },
- enter = enterTransition(index = index, totalCount = viewModels.size),
- exit = exitTransition(index = index, totalCount = viewModels.size)
- ) {
- VolumeSlider(
- modifier = Modifier.fillMaxWidth(),
- state = sliderState,
- onValueChange = { newValue: Float ->
- sliderViewModel.onValueChanged(sliderState, newValue)
- },
- onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
- onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
- sliderColors = sliderColors,
- )
- }
+
+ VolumeSlider(
+ modifier =
+ Modifier.padding(top = 16.dp)
+ .fillMaxWidth()
+ .animateEnterExit(
+ enter =
+ enterTransition(
+ index = index,
+ totalCount = viewModels.size,
+ ),
+ exit =
+ exitTransition(
+ index = index,
+ totalCount = viewModels.size,
+ ),
+ ),
+ state = sliderState,
+ onValueChange = { newValue: Float ->
+ sliderViewModel.onValueChanged(sliderState, newValue)
+ },
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
+ onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
+ sliderColors = sliderColors,
+ )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 5ffb6f8..1cc0fb2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -25,13 +25,13 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.res.R
import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
@@ -43,7 +43,6 @@
private val padding = 24.dp
@Composable
-@OptIn(ExperimentalComposeUiApi::class)
fun VolumePanelRoot(
viewModel: VolumePanelViewModel,
modifier: Modifier = Modifier,
@@ -54,18 +53,20 @@
with(VolumePanelComposeScope(state)) {
components?.let { componentsState ->
- Components(
- componentsState,
- modifier
- .sysuiResTag(VolumePanelTestTag)
- .semantics { paneTitle = accessibilityTitle }
- .padding(
- start = padding,
- top = padding,
- end = padding,
- bottom = 20.dp,
- )
- )
+ PlatformTheme {
+ Components(
+ componentsState,
+ modifier
+ .sysuiResTag(VolumePanelTestTag)
+ .semantics { paneTitle = accessibilityTitle }
+ .padding(
+ start = padding,
+ top = padding,
+ end = padding,
+ bottom = 20.dp,
+ )
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
index b166737..d876606 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
@@ -21,9 +21,7 @@
import androidx.compose.animation.core.SpringSpec
import com.android.compose.animation.scene.content.state.TransitionState
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
internal fun CoroutineScope.animateContent(
layoutState: MutableSceneTransitionLayoutStateImpl,
@@ -31,37 +29,24 @@
oneOffAnimation: OneOffAnimation,
targetProgress: Float,
chain: Boolean = true,
-) {
- // Start the transition. This will compute the TransformationSpec associated to [transition],
- // which we need to initialize the Animatable that will actually animate it.
- layoutState.startTransition(transition, chain)
-
- // The transition now contains the transformation spec that we should use to instantiate the
- // Animatable.
- val animationSpec = transition.transformationSpec.progressSpec
- val visibilityThreshold =
- (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
- val replacedTransition = transition.replacedTransition
- val initialProgress = replacedTransition?.progress ?: 0f
- val initialVelocity = replacedTransition?.progressVelocity ?: 0f
- val animatable =
- Animatable(initialProgress, visibilityThreshold = visibilityThreshold).also {
- oneOffAnimation.animatable = it
- }
-
- // Animate the progress to its target value.
- //
- // Important: We start atomically to make sure that we start the coroutine even if it is
- // cancelled right after it is launched, so that finishTransition() is correctly called.
- // Otherwise, this transition will never be stopped and we will never settle to Idle.
- oneOffAnimation.job =
- launch(start = CoroutineStart.ATOMIC) {
- try {
- animatable.animateTo(targetProgress, animationSpec, initialVelocity)
- } finally {
- layoutState.finishTransition(transition)
+): Job {
+ oneOffAnimation.onRun = {
+ // Animate the progress to its target value.
+ val animationSpec = transition.transformationSpec.progressSpec
+ val visibilityThreshold =
+ (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
+ val replacedTransition = transition.replacedTransition
+ val initialProgress = replacedTransition?.progress ?: 0f
+ val initialVelocity = replacedTransition?.progressVelocity ?: 0f
+ val animatable =
+ Animatable(initialProgress, visibilityThreshold = visibilityThreshold).also {
+ oneOffAnimation.animatable = it
}
- }
+
+ animatable.animateTo(targetProgress, animationSpec, initialVelocity)
+ }
+
+ return layoutState.startTransitionImmediately(animationScope = this, transition, chain)
}
internal class OneOffAnimation {
@@ -74,8 +59,8 @@
*/
lateinit var animatable: Animatable<Float, AnimationVector1D>
- /** The job that is animating [animatable]. */
- lateinit var job: Job
+ /** The runnable to run for this animation. */
+ lateinit var onRun: suspend () -> Unit
val progress: Float
get() = animatable.value
@@ -83,7 +68,13 @@
val progressVelocity: Float
get() = animatable.velocity
- fun finish(): Job = job
+ suspend fun run() {
+ onRun()
+ }
+
+ fun freezeAndAnimateToCurrentState() {
+ // Do nothing, the state of one-off animations never change and we directly animate to it.
+ }
}
// TODO(b/290184746): Compute a good default visibility threshold that depends on the layout size
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt
index e020f14..28116cb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt
@@ -18,7 +18,6 @@
import com.android.compose.animation.scene.content.state.TransitionState
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
/** Trigger a one-off transition to show or hide an overlay. */
internal fun CoroutineScope.showOrHideOverlay(
@@ -120,7 +119,13 @@
override val isInitiatedByUserInput: Boolean = false
override val isUserInputOngoing: Boolean = false
- override fun finish(): Job = oneOffAnimation.finish()
+ override suspend fun run() {
+ oneOffAnimation.run()
+ }
+
+ override fun freezeAndAnimateToCurrentState() {
+ oneOffAnimation.freezeAndAnimateToCurrentState()
+ }
}
private class OneOffOverlayReplacingTransition(
@@ -140,5 +145,11 @@
override val isInitiatedByUserInput: Boolean = false
override val isUserInputOngoing: Boolean = false
- override fun finish(): Job = oneOffAnimation.finish()
+ override suspend fun run() {
+ oneOffAnimation.run()
+ }
+
+ override fun freezeAndAnimateToCurrentState() {
+ oneOffAnimation.freezeAndAnimateToCurrentState()
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 4aa50b5..0fc88b2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -121,7 +121,7 @@
@Deprecated(
"Use animateContentFloatAsState() instead",
- replaceWith = ReplaceWith("animateContentFloatAsState(value, key, canOverflow)")
+ replaceWith = ReplaceWith("animateContentFloatAsState(value, key, canOverflow)"),
)
@Composable
fun ContentScope.animateSceneFloatAsState(
@@ -172,14 +172,11 @@
@Deprecated(
"Use animateContentDpAsState() instead",
- replaceWith = ReplaceWith("animateContentDpAsState(value, key, canOverflow)")
+ replaceWith = ReplaceWith("animateContentDpAsState(value, key, canOverflow)"),
)
@Composable
-fun ContentScope.animateSceneDpAsState(
- value: Dp,
- key: ValueKey,
- canOverflow: Boolean = true,
-) = animateContentDpAsState(value, key, canOverflow)
+fun ContentScope.animateSceneDpAsState(value: Dp, key: ValueKey, canOverflow: Boolean = true) =
+ animateContentDpAsState(value, key, canOverflow)
/**
* Animate a shared element Dp value.
@@ -214,10 +211,7 @@
* @see ContentScope.animateContentValueAsState
*/
@Composable
-fun ContentScope.animateContentColorAsState(
- value: Color,
- key: ValueKey,
-): AnimatedState<Color> {
+fun ContentScope.animateContentColorAsState(value: Color, key: ValueKey): AnimatedState<Color> {
return animateContentValueAsState(value, key, SharedColorType, canOverflow = false)
}
@@ -227,10 +221,7 @@
* @see ElementScope.animateElementValueAsState
*/
@Composable
-fun ElementScope<*>.animateElementColorAsState(
- value: Color,
- key: ValueKey,
-): AnimatedState<Color> {
+fun ElementScope<*>.animateElementColorAsState(value: Color, key: ValueKey): AnimatedState<Color> {
return animateElementValueAsState(value, key, SharedColorType, canOverflow = false)
}
@@ -274,12 +265,7 @@
* Note: This class is necessary because Color() checks the bounds of its values and UncheckedColor
* is internal.
*/
-private class ColorDelta(
- val red: Float,
- val green: Float,
- val blue: Float,
- val alpha: Float,
-)
+private class ColorDelta(val red: Float, val green: Float, val blue: Float, val alpha: Float)
@Composable
internal fun <T> animateSharedValueAsState(
@@ -331,7 +317,7 @@
private fun <T, Delta> sharedValue(
layoutImpl: SceneTransitionLayoutImpl,
key: ValueKey,
- element: ElementKey?
+ element: ElementKey?,
): SharedValue<T, Delta> {
return layoutImpl.sharedValues[key]?.get(element)?.let { it as SharedValue<T, Delta> }
?: error(valueReadTooEarlyMessage(key))
@@ -342,9 +328,7 @@
"means that you are reading it during composition, which you should not do. See the " +
"documentation of AnimatedState for more information."
-internal class SharedValue<T, Delta>(
- val type: SharedValueType<T, Delta>,
-) {
+internal class SharedValue<T, Delta>(val type: SharedValueType<T, Delta>) {
/** The target value of this shared value for each content. */
val targetValues = SnapshotStateMap<ContentKey, T>()
@@ -399,26 +383,53 @@
val fromValue = sharedValue[transition.fromContent]
val toValue = sharedValue[transition.toContent]
- return if (fromValue != null && toValue != null) {
- if (fromValue == toValue) {
- // Optimization: avoid reading progress if the values are the same, so we don't
- // relayout/redraw for nothing.
- fromValue
- } else {
- val overscrollSpec = transition.currentOverscrollSpec
- val progress =
- when {
- overscrollSpec == null -> {
- if (canOverflow) transition.progress
- else transition.progress.fastCoerceIn(0f, 1f)
- }
- overscrollSpec.content == transition.toContent -> 1f
- else -> 0f
- }
+ if (fromValue == null && toValue == null) {
+ return null
+ }
- sharedValue.type.lerp(fromValue, toValue, progress)
+ if (fromValue != null && toValue != null) {
+ return interpolateSharedValue(fromValue, toValue, transition, sharedValue)
+ }
+
+ if (transition is TransitionState.Transition.ReplaceOverlay) {
+ val currentSceneValue = sharedValue[transition.currentScene]
+ if (currentSceneValue != null) {
+ return interpolateSharedValue(
+ fromValue = fromValue ?: currentSceneValue,
+ toValue = toValue ?: currentSceneValue,
+ transition,
+ sharedValue,
+ )
}
- } else fromValue ?: toValue
+ }
+
+ return fromValue ?: toValue
+ }
+
+ private fun interpolateSharedValue(
+ fromValue: T,
+ toValue: T,
+ transition: TransitionState.Transition,
+ sharedValue: SharedValue<T, *>,
+ ): T? {
+ if (fromValue == toValue) {
+ // Optimization: avoid reading progress if the values are the same, so we don't
+ // relayout/redraw for nothing.
+ return fromValue
+ }
+
+ val overscrollSpec = transition.currentOverscrollSpec
+ val progress =
+ when {
+ overscrollSpec == null -> {
+ if (canOverflow) transition.progress
+ else transition.progress.fastCoerceIn(0f, 1f)
+ }
+ overscrollSpec.content == transition.toContent -> 1f
+ else -> 0f
+ }
+
+ return sharedValue.type.lerp(fromValue, toValue, progress)
}
private fun transition(sharedValue: SharedValue<T, Delta>): TransitionState.Transition? {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index e15bc12..86be4a4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -28,7 +28,7 @@
layoutState: MutableSceneTransitionLayoutStateImpl,
target: SceneKey,
transitionKey: TransitionKey?,
-): TransitionState.Transition.ChangeScene? {
+): Pair<TransitionState.Transition.ChangeScene, Job>? {
val transitionState = layoutState.transitionState
if (transitionState.currentScene == target) {
// This can happen in 3 different situations, for which there isn't anything else to do:
@@ -139,7 +139,7 @@
reversed: Boolean = false,
fromScene: SceneKey = layoutState.transitionState.currentScene,
chain: Boolean = true,
-): TransitionState.Transition.ChangeScene {
+): Pair<TransitionState.Transition.ChangeScene, Job> {
val oneOffAnimation = OneOffAnimation()
val targetProgress = if (reversed) 0f else 1f
val transition =
@@ -165,15 +165,16 @@
)
}
- animateContent(
- layoutState = layoutState,
- transition = transition,
- oneOffAnimation = oneOffAnimation,
- targetProgress = targetProgress,
- chain = chain,
- )
+ val job =
+ animateContent(
+ layoutState = layoutState,
+ transition = transition,
+ oneOffAnimation = oneOffAnimation,
+ targetProgress = targetProgress,
+ chain = chain,
+ )
- return transition
+ return transition to job
}
private class OneOffSceneTransition(
@@ -193,5 +194,11 @@
override val isUserInputOngoing: Boolean = false
- override fun finish(): Job = oneOffAnimation.finish()
+ override suspend fun run() {
+ oneOffAnimation.run()
+ }
+
+ override fun freezeAndAnimateToCurrentState() {
+ oneOffAnimation.freezeAndAnimateToCurrentState()
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 37e4daa..f38a310 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -28,7 +28,6 @@
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import kotlin.math.absoluteValue
-import kotlinx.coroutines.CoroutineScope
internal interface DraggableHandler {
/**
@@ -63,7 +62,6 @@
internal class DraggableHandlerImpl(
internal val layoutImpl: SceneTransitionLayoutImpl,
internal val orientation: Orientation,
- internal val coroutineScope: CoroutineScope,
) : DraggableHandler {
internal val nestedScrollKey = Any()
/** The [DraggableHandler] can only have one active [DragController] at a time. */
@@ -101,11 +99,6 @@
val swipeAnimation = dragController.swipeAnimation
- // Don't intercept a transition that is finishing.
- if (swipeAnimation.isFinishing) {
- return false
- }
-
// Only intercept the current transition if one of the 2 swipes results is also a transition
// between the same pair of contents.
val swipes = computeSwipes(startedPosition, pointersDown = 1)
@@ -116,12 +109,12 @@
return (upOrLeft != null &&
contentTransition.isTransitioningBetween(
fromContent.key,
- upOrLeft.toContent(currentScene)
+ upOrLeft.toContent(currentScene),
)) ||
(downOrRight != null &&
contentTransition.isTransitioningBetween(
fromContent.key,
- downOrRight.toContent(currentScene)
+ downOrRight.toContent(currentScene),
))
}
@@ -140,7 +133,6 @@
// This [transition] was already driving the animation: simply take over it.
// Stop animating and start from the current offset.
val oldSwipeAnimation = oldDragController.swipeAnimation
- oldSwipeAnimation.cancelOffsetAnimation()
// We need to recompute the swipe results since this is a new gesture, and the
// fromScene.userActions may have changed.
@@ -171,7 +163,7 @@
private fun updateDragController(
swipes: Swipes,
- swipeAnimation: SwipeAnimation<*>
+ swipeAnimation: SwipeAnimation<*>,
): DragControllerImpl {
val newDragController = DragControllerImpl(this, swipes, swipeAnimation)
newDragController.updateTransition(swipeAnimation, force = true)
@@ -179,10 +171,7 @@
return newDragController
}
- internal fun createSwipeAnimation(
- swipes: Swipes,
- result: UserActionResult,
- ): SwipeAnimation<*> {
+ internal fun createSwipeAnimation(swipes: Swipes, result: UserActionResult): SwipeAnimation<*> {
val upOrLeftResult = swipes.upOrLeftResult
val downOrRightResult = swipes.downOrRightResult
val isUpOrLeft =
@@ -192,26 +181,18 @@
else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)")
}
- return createSwipeAnimation(
- layoutImpl,
- layoutImpl.coroutineScope,
- result,
- isUpOrLeft,
- orientation
- )
+ return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation)
}
private fun computeSwipes(startedPosition: Offset?, pointersDown: Int): Swipes {
val fromSource =
startedPosition?.let { position ->
- layoutImpl.swipeSourceDetector
- .source(
- layoutImpl.lastSize,
- position.round(),
- layoutImpl.density,
- orientation,
- )
- ?.resolve(layoutImpl.layoutDirection)
+ layoutImpl.swipeSourceDetector.source(
+ layoutImpl.lastSize,
+ position.round(),
+ layoutImpl.density,
+ orientation,
+ )
}
val upOrLeft =
@@ -279,16 +260,14 @@
fun updateTransition(newTransition: SwipeAnimation<*>, force: Boolean = false) {
if (force || isDrivingTransition) {
- layoutState.startTransition(newTransition.contentTransition)
+ layoutState.startTransitionImmediately(
+ animationScope = draggableHandler.layoutImpl.animationScope,
+ newTransition.contentTransition,
+ true,
+ )
}
- val previous = swipeAnimation
swipeAnimation = newTransition
-
- // Finish the previous transition.
- if (previous != newTransition) {
- layoutState.finishTransition(previous.contentTransition)
- }
}
/**
@@ -302,7 +281,7 @@
}
private fun <T : ContentKey> onDrag(delta: Float, swipeAnimation: SwipeAnimation<T>): Float {
- if (delta == 0f || !isDrivingTransition || swipeAnimation.isFinishing) {
+ if (delta == 0f || !isDrivingTransition || swipeAnimation.isAnimatingOffset()) {
return 0f
}
@@ -409,18 +388,12 @@
swipeAnimation: SwipeAnimation<T>,
): Float {
// The state was changed since the drag started; don't do anything.
- if (!isDrivingTransition || swipeAnimation.isFinishing) {
+ if (!isDrivingTransition || swipeAnimation.isAnimatingOffset()) {
return 0f
}
- fun animateTo(targetContent: T) {
- swipeAnimation.animateOffset(
- initialVelocity = velocity,
- targetContent = targetContent,
- )
- }
-
val fromContent = swipeAnimation.fromContent
+ val consumedVelocity: Float
if (canChangeContent) {
// If we are halfway between two contents, we check what the target will be based on the
// velocity and offset of the transition, then we launch the animation.
@@ -445,18 +418,16 @@
} else {
fromContent
}
-
- animateTo(targetContent = targetContent)
+ consumedVelocity = swipeAnimation.animateOffset(velocity, targetContent = targetContent)
} else {
// We are doing an overscroll preview animation between scenes.
check(fromContent == swipeAnimation.currentContent) {
"canChangeContent is false but currentContent != fromContent"
}
- animateTo(targetContent = fromContent)
+ consumedVelocity = swipeAnimation.animateOffset(velocity, targetContent = fromContent)
}
- // The onStop animation consumes any remaining velocity.
- return velocity
+ return consumedVelocity
}
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
index 97c0cef..edd697b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
@@ -54,23 +54,23 @@
position: IntOffset,
density: Density,
orientation: Orientation,
- ): Edge? {
+ ): Edge.Resolved? {
val axisSize: Int
val axisPosition: Int
- val topOrLeft: Edge
- val bottomOrRight: Edge
+ val topOrLeft: Edge.Resolved
+ val bottomOrRight: Edge.Resolved
when (orientation) {
Orientation.Horizontal -> {
axisSize = layoutSize.width
axisPosition = position.x
- topOrLeft = Edge.Left
- bottomOrRight = Edge.Right
+ topOrLeft = Edge.Resolved.Left
+ bottomOrRight = Edge.Resolved.Right
}
Orientation.Vertical -> {
axisSize = layoutSize.height
axisPosition = position.y
- topOrLeft = Edge.Top
- bottomOrRight = Edge.Bottom
+ topOrLeft = Edge.Resolved.Top
+ bottomOrRight = Edge.Resolved.Bottom
}
}
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 9b1740d..5a084db 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)
@@ -263,7 +263,7 @@
@ExperimentalComposeUiApi
override fun MeasureScope.measure(
measurable: Measurable,
- constraints: Constraints
+ constraints: Constraints,
): MeasureResult {
check(isLookingAhead)
@@ -313,10 +313,27 @@
// If this element is not supposed to be laid out now, either because it is not part of any
// ongoing transition or the other content of its transition is overscrolling, then lay out
// the element normally and don't place it.
- val overscrollScene = transition?.currentOverscrollSpec?.content
- val isOtherSceneOverscrolling = overscrollScene != null && overscrollScene != content.key
- if (isOtherSceneOverscrolling) {
- return doNotPlace(measurable, constraints)
+ val overscrollContent = transition?.currentOverscrollSpec?.content
+ if (overscrollContent != null && overscrollContent != content.key) {
+ when (transition) {
+ is TransitionState.Transition.ChangeScene ->
+ return doNotPlace(measurable, constraints)
+
+ // If we are overscrolling an overlay that does not contain an element that is in
+ // the current scene, place it in that scene otherwise the element won't be placed
+ // at all.
+ is TransitionState.Transition.ShowOrHideOverlay,
+ is TransitionState.Transition.ReplaceOverlay -> {
+ if (
+ content.key == transition.currentScene &&
+ overscrollContent !in element.stateByContent
+ ) {
+ return placeNormally(measurable, constraints)
+ } else {
+ return doNotPlace(measurable, constraints)
+ }
+ }
+ }
}
val placeable =
@@ -327,7 +344,7 @@
private fun ApproachMeasureScope.doNotPlace(
measurable: Measurable,
- constraints: Constraints
+ constraints: Constraints,
): MeasureResult {
recursivelyClearPlacementValues()
stateInContent.lastSize = Element.SizeUnspecified
@@ -338,7 +355,7 @@
private fun ApproachMeasureScope.placeNormally(
measurable: Measurable,
- constraints: Constraints
+ constraints: Constraints,
): MeasureResult {
val placeable = measurable.measure(constraints)
stateInContent.lastSize = placeable.size()
@@ -653,10 +670,7 @@
* Reconcile the state of [element] in the formContent and toContent of [transition] so that the
* values before interruption have their expected values, taking shared transitions into account.
*/
-private fun reconcileStates(
- element: Element,
- transition: TransitionState.Transition,
-) {
+private fun reconcileStates(element: Element, transition: TransitionState.Transition) {
val fromContentState = element.stateByContent[transition.fromContent] ?: return
val toContentState = element.stateByContent[transition.toContent] ?: return
if (!isSharedElementEnabled(element.key, transition)) {
@@ -814,15 +828,21 @@
// Don't place the element in this content if this content is not part of the current element
// transition.
- if (content != transition.fromContent && content != transition.toContent) {
+ val isReplacingOverlay = transition is TransitionState.Transition.ReplaceOverlay
+ if (
+ content != transition.fromContent &&
+ content != transition.toContent &&
+ (!isReplacingOverlay || content != transition.currentScene)
+ ) {
return false
}
// Place the element if it is not shared.
- if (
- transition.fromContent !in element.stateByContent ||
- transition.toContent !in element.stateByContent
- ) {
+ var copies = 0
+ if (transition.fromContent in element.stateByContent) copies++
+ if (transition.toContent in element.stateByContent) copies++
+ if (isReplacingOverlay && transition.currentScene in element.stateByContent) copies++
+ if (copies <= 1) {
return true
}
@@ -836,19 +856,32 @@
content,
element.key,
transition,
+ isInContent = { it in element.stateByContent },
)
}
-internal fun shouldPlaceOrComposeSharedElement(
+internal inline fun shouldPlaceOrComposeSharedElement(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
+ isInContent: (ContentKey) -> Boolean,
): Boolean {
- // If we are overscrolling, only place/compose the element in the overscrolling scene.
- val overscrollScene = transition.currentOverscrollSpec?.content
- if (overscrollScene != null) {
- return content == overscrollScene
+ val overscrollContent = transition.currentOverscrollSpec?.content
+ if (overscrollContent != null) {
+ return when (transition) {
+ // If we are overscrolling between scenes, only place/compose the element in the
+ // overscrolling scene.
+ is TransitionState.Transition.ChangeScene -> content == overscrollContent
+
+ // If we are overscrolling an overlay, place/compose the element if [content] is the
+ // overscrolling content or if [content] is the current scene and the overscrolling
+ // overlay does not contain the element.
+ is TransitionState.Transition.ReplaceOverlay,
+ is TransitionState.Transition.ShowOrHideOverlay ->
+ content == overscrollContent ||
+ (content == transition.currentScene && !isInContent(overscrollContent))
+ }
}
val scenePicker = element.contentPicker
@@ -1109,7 +1142,7 @@
Offset.Unspecified
} else {
a.pivot.specifiedOrCenter() - b.pivot.specifiedOrCenter()
- }
+ },
)
},
add = { a, b, bProgress ->
@@ -1121,9 +1154,9 @@
Offset.Unspecified
} else {
a.pivot.specifiedOrCenter() + b.pivot.specifiedOrCenter() * bProgress
- }
+ },
)
- }
+ },
)
stateInContent.lastScale = interruptedScale
@@ -1230,17 +1263,31 @@
// elements follow the finger direction.
val isSharedElement = fromState != null && toState != null
if (isSharedElement && isSharedElementEnabled(element.key, transition)) {
- val start = contentValue(fromState!!)
- val end = contentValue(toState!!)
+ return interpolateSharedElement(
+ transition = transition,
+ contentValue = contentValue,
+ fromState = fromState!!,
+ toState = toState!!,
+ isSpecified = isSpecified,
+ lerp = lerp,
+ )
+ }
- // TODO(b/316901148): Remove checks to isSpecified() once the lookahead pass runs for all
- // nodes before the intermediate layout pass.
- if (!isSpecified(start)) return end
- if (!isSpecified(end)) return start
-
- // Make sure we don't read progress if values are the same and we don't need to interpolate,
- // so we don't invalidate the phase where this is read.
- return if (start == end) start else lerp(start, end, transition.progress)
+ // If we are replacing an overlay and the element is both in a single overlay and in the current
+ // scene, interpolate the state of the element using the current scene as the other scene.
+ var currentSceneState: Element.State? = null
+ if (!isSharedElement && transition is TransitionState.Transition.ReplaceOverlay) {
+ currentSceneState = element.stateByContent[transition.currentScene]
+ if (currentSceneState != null && isSharedElementEnabled(element.key, transition)) {
+ return interpolateSharedElement(
+ transition = transition,
+ contentValue = contentValue,
+ fromState = fromState ?: currentSceneState,
+ toState = toState ?: currentSceneState,
+ isSpecified = isSpecified,
+ lerp = lerp,
+ )
+ }
}
// Get the transformed value, i.e. the target value at the beginning (for entering elements) or
@@ -1250,6 +1297,8 @@
when {
isSharedElement && currentContent == fromContent -> fromState
isSharedElement -> toState
+ currentSceneState != null && currentContent == transition.currentScene ->
+ currentSceneState
else -> fromState ?: toState
}
)
@@ -1328,7 +1377,7 @@
lerp(
lerp(previewTargetValue, targetValueOrNull ?: idleValue, previewRangeProgress),
idleValue,
- transformation?.range?.progress(transition.progress) ?: transition.progress
+ transformation?.range?.progress(transition.progress) ?: transition.progress,
)
} else {
if (targetValueOrNull == null) {
@@ -1341,7 +1390,7 @@
lerp(
lerp(idleValue, previewTargetValue, previewRangeProgress),
targetValueOrNull,
- transformation.range?.progress(transition.progress) ?: transition.progress
+ transformation.range?.progress(transition.progress) ?: transition.progress,
)
}
}
@@ -1356,14 +1405,7 @@
val idleValue = contentValue(contentState)
val targetValue =
- transformation.transform(
- layoutImpl,
- content,
- element,
- contentState,
- transition,
- idleValue,
- )
+ transformation.transform(layoutImpl, content, element, contentState, transition, idleValue)
// Make sure we don't read progress if values are the same and we don't need to interpolate, so
// we don't invalidate the phase where this is read.
@@ -1376,10 +1418,37 @@
val rangeProgress = transformation.range?.progress(progress) ?: progress
// Interpolate between the value at rest and the value before entering/after leaving.
- val isEntering = content == toContent
+ val isEntering =
+ when {
+ content == toContent -> true
+ content == fromContent -> false
+ content == transition.currentScene -> toState == null
+ else -> content == toContent
+ }
return if (isEntering) {
lerp(targetValue, idleValue, rangeProgress)
} else {
lerp(idleValue, targetValue, rangeProgress)
}
}
+
+private inline fun <T> interpolateSharedElement(
+ transition: TransitionState.Transition,
+ contentValue: (Element.State) -> T,
+ fromState: Element.State,
+ toState: Element.State,
+ isSpecified: (T) -> Boolean,
+ lerp: (T, T, Float) -> T,
+): T {
+ val start = contentValue(fromState)
+ val end = contentValue(toState)
+
+ // TODO(b/316901148): Remove checks to isSpecified() once the lookahead pass runs for all
+ // nodes before the intermediate layout pass.
+ if (!isSpecified(start)) return end
+ if (!isSpecified(end)) return start
+
+ // Make sure we don't read progress if values are the same and we don't need to interpolate,
+ // so we don't invalidate the phase where this is read.
+ return if (start == end) start else lerp(start, end, transition.progress)
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
index cb18c67..2057146 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
@@ -79,9 +79,6 @@
interrupted: TransitionState.Transition.ChangeScene,
newTargetScene: SceneKey,
): InterruptionResult {
- return InterruptionResult(
- animateFrom = interrupted.currentScene,
- chain = true,
- )
+ return InterruptionResult(animateFrom = interrupted.currentScene, chain = true)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
index 3f8f5e7..f9a9eeb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
@@ -49,10 +49,7 @@
}
/** Key for a scene. */
-class SceneKey(
- debugName: String,
- identity: Any = Object(),
-) : ContentKey(debugName, identity) {
+class SceneKey(debugName: String, identity: Any = Object()) : ContentKey(debugName, identity) {
override val testTag: String = "scene:$debugName"
/** The unique [ElementKey] identifying this scene's root element. */
@@ -64,10 +61,7 @@
}
/** Key for an overlay. */
-class OverlayKey(
- debugName: String,
- identity: Any = Object(),
-) : ContentKey(debugName, identity) {
+class OverlayKey(debugName: String, identity: Any = Object()) : ContentKey(debugName, identity) {
override val testTag: String = "overlay:$debugName"
override fun toString(): String {
@@ -153,4 +147,15 @@
override fun toString(): String {
return "TransitionKey(debugName=$debugName)"
}
+
+ companion object {
+ /**
+ * A special transition key indicating that the associated transition should be used for
+ * Predictive Back gestures.
+ *
+ * Use this key when defining a transition that you want to be specifically triggered when
+ * the user performs a Predictive Back gesture.
+ */
+ val PredictiveBack = TransitionKey("PredictiveBack")
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 715222c..1f26b71 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -86,7 +86,7 @@
value: T,
key: ValueKey,
type: SharedValueType<T, *>,
- canOverflow: Boolean
+ canOverflow: Boolean,
): AnimatedState<T> {
return animateSharedValueAsState(
layoutImpl,
@@ -194,11 +194,13 @@
is TransitionState.Transition -> {
// During transitions, always compose movable elements in the scene picked by their
// content picker.
+ val contents = element.contentPicker.contents
shouldPlaceOrComposeSharedElement(
layoutImpl,
content,
element,
elementState,
+ isInContent = { contents.contains(it) },
)
}
}
@@ -208,8 +210,8 @@
element: MovableElementKey,
transitionStates: List<TransitionState>,
): TransitionState? {
- val content = element.contentPicker.contents
- return elementState(transitionStates, isInContent = { content.contains(it) })
+ val contents = element.contentPicker.contents
+ return elementState(transitionStates, isInContent = { contents.contains(it) })
}
private fun movableElementContentWhenIdle(
@@ -218,11 +220,7 @@
elementState: TransitionState.Idle,
): ContentKey {
val contents = element.contentPicker.contents
- return elementContentWhenIdle(
- layoutImpl,
- elementState,
- isInContent = { contents.contains(it) },
- )
+ return elementContentWhenIdle(layoutImpl, elementState, isInContent = { contents.contains(it) })
}
/**
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 fd4c310..fb9dde3 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
@@ -56,7 +56,7 @@
import com.android.compose.ui.util.SpaceVectorConverter
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.sign
-import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
@@ -143,8 +143,8 @@
CompositionLocalConsumerModifierNode,
ObserverModifierNode,
SpaceVectorConverter {
- private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() }
- private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler))
+ private val pointerTracker = delegate(SuspendingPointerInputModifierNode { pointerTracker() })
+ private val pointerInput = delegate(SuspendingPointerInputModifierNode { pointerInput() })
private val velocityTracker = VelocityTracker()
private var previousEnabled: Boolean = false
@@ -153,7 +153,7 @@
// Reset the pointer input whenever enabled changed.
if (value != field) {
field = value
- delegate.resetPointerInputHandler()
+ pointerInput.resetPointerInputHandler()
}
}
@@ -173,7 +173,7 @@
if (value != field) {
field = value
converter = SpaceVectorConverter(value)
- delegate.resetPointerInputHandler()
+ pointerInput.resetPointerInputHandler()
}
}
@@ -186,19 +186,26 @@
observeReads {
val newEnabled = enabled()
if (newEnabled != previousEnabled) {
- delegate.resetPointerInputHandler()
+ pointerInput.resetPointerInputHandler()
}
previousEnabled = newEnabled
}
}
- override fun onCancelPointerInput() = delegate.onCancelPointerInput()
+ override fun onCancelPointerInput() {
+ pointerTracker.onCancelPointerInput()
+ pointerInput.onCancelPointerInput()
+ }
override fun onPointerEvent(
pointerEvent: PointerEvent,
pass: PointerEventPass,
- bounds: IntSize
- ) = delegate.onPointerEvent(pointerEvent, pass, bounds)
+ bounds: IntSize,
+ ) {
+ // The order is important here: the tracker is always called first.
+ pointerTracker.onPointerEvent(pointerEvent, pass, bounds)
+ pointerInput.onPointerEvent(pointerEvent, pass, bounds)
+ }
private var startedPosition: Offset? = null
private var pointersDown: Int = 0
@@ -211,81 +218,120 @@
)
}
+ 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 changes = awaitPointerEvent(PointerEventPass.Initial).changes
+ pointersDown = changes.countDown()
+
+ when {
+ // There are no more pointers down.
+ pointersDown == 0 -> {
+ startedPosition = null
+
+ val lastPointerUp = changes.single { it.id == velocityPointerId }
+ velocityTracker.addPointerInputChange(lastPointerUp)
+ }
+
+ // 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 =
+ if (pointerChange.pressed) {
+ pointerChange.id
+ } else {
+ changes.first { it.pressed }.id
+ }
+
+ velocityTracker.addPointerInputChange(pointerChange)
+ }
+ }
+ }
+ }
+ }
+
private suspend fun PointerInputScope.pointerInput() {
if (!enabled()) {
return
}
- coroutineScope {
- launch {
- // Intercepts pointer inputs and exposes [PointersInfo], via
- // [requireAncestorPointersInfoOwner], to our descendants.
- awaitPointerEventScope {
- while (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
- onFirstPointerDown()
- }
- }
- }
- }
-
- // The order is important here: we want to make sure that the previous PointerEventScope
- // is initialized first. This ensures that the following PointerEventScope doesn't
- // receive more events than the first one.
- launch {
- awaitPointerEventScope {
- while (isActive) {
- try {
- detectDragGestures(
- orientation = orientation,
- startDragImmediately = startDragImmediately,
- onDragStart = { startedPosition, overSlop, pointersDown ->
- velocityTracker.resetTracking()
- onDragStarted(startedPosition, overSlop, pointersDown)
- },
- onDrag = { controller, change, amount ->
- velocityTracker.addPointerInputChange(change)
- dispatchScrollEvents(
- availableOnPreScroll = amount,
- onScroll = { controller.onDrag(it) },
- source = NestedScrollSource.UserInput,
- )
- },
- onDragEnd = { controller ->
- startFlingGesture(
- initialVelocity =
- currentValueOf(LocalViewConfiguration)
- .maximumFlingVelocity
- .let {
- val maxVelocity = Velocity(it, it)
- velocityTracker.calculateVelocity(maxVelocity)
- }
- .toFloat(),
- onFling = { controller.onStop(it, canChangeContent = true) }
- )
- },
- onDragCancel = { controller ->
- startFlingGesture(
- initialVelocity = 0f,
- onFling = { controller.onStop(it, canChangeContent = true) }
- )
- },
- swipeDetector = swipeDetector,
+ val currentContext = currentCoroutineContext()
+ awaitPointerEventScope {
+ while (currentContext.isActive) {
+ try {
+ detectDragGestures(
+ orientation = orientation,
+ startDragImmediately = startDragImmediately,
+ onDragStart = { startedPosition, overSlop, pointersDown ->
+ onDragStarted(startedPosition, overSlop, pointersDown)
+ },
+ onDrag = { controller, amount ->
+ dispatchScrollEvents(
+ availableOnPreScroll = amount,
+ onScroll = { controller.onDrag(it) },
+ source = NestedScrollSource.UserInput,
)
- } catch (exception: CancellationException) {
- // If the coroutine scope is active, we can just restart the drag cycle.
- if (!isActive) {
- throw exception
- }
- }
+ },
+ onDragEnd = { controller ->
+ startFlingGesture(
+ initialVelocity =
+ currentValueOf(LocalViewConfiguration)
+ .maximumFlingVelocity
+ .let {
+ val maxVelocity = Velocity(it, it)
+ velocityTracker.calculateVelocity(maxVelocity)
+ }
+ .toFloat(),
+ onFling = { controller.onStop(it, canChangeContent = true) },
+ )
+ },
+ onDragCancel = { controller ->
+ startFlingGesture(
+ initialVelocity = 0f,
+ onFling = { controller.onStop(it, canChangeContent = true) },
+ )
+ },
+ swipeDetector = swipeDetector,
+ )
+ } catch (exception: CancellationException) {
+ // If the coroutine scope is active, we can just restart the drag cycle.
+ if (!currentContext.isActive) {
+ throw exception
}
}
}
@@ -328,10 +374,7 @@
// PreScroll phase
val consumedByPreScroll =
dispatcher
- .dispatchPreScroll(
- available = availableOnPreScroll.toOffset(),
- source = source,
- )
+ .dispatchPreScroll(available = availableOnPreScroll.toOffset(), source = source)
.toFloat()
// Scroll phase
@@ -400,7 +443,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,
@@ -443,12 +486,12 @@
Orientation.Horizontal ->
awaitHorizontalTouchSlopOrCancellation(
consumablePointer.id,
- onSlopReached
+ onSlopReached,
)
Orientation.Vertical ->
awaitVerticalTouchSlopOrCancellation(
consumablePointer.id,
- onSlopReached
+ onSlopReached,
)
}
@@ -479,14 +522,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 = {
@@ -512,7 +555,7 @@
}
private suspend fun AwaitPointerEventScope.awaitConsumableEvent(
- pass: () -> PointerEventPass,
+ pass: () -> PointerEventPass
): PointerEvent {
fun canBeConsumed(changes: List<PointerInputChange>): Boolean {
// At least one pointer down AND
@@ -620,7 +663,4 @@
fun pointersInfo(): PointersInfo
}
-internal data class PointersInfo(
- val startedPosition: Offset?,
- val pointersDown: Int,
-)
+internal data class PointersInfo(val startedPosition: Offset?, val pointersDown: Int)
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 3a7c2bf..30eb302 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
@@ -31,9 +31,6 @@
* layout or Compose drawing phases.
* 2. [ObservableTransitionState] values are backed by Kotlin [Flow]s and can be collected by
* non-Compose code to observe value changes.
- * 3. [ObservableTransitionState.Transition.fromScene] and
- * [ObservableTransitionState.Transition.toScene] will never be equal, while
- * [TransitionState.Transition.fromScene] and [TransitionState.Transition.toScene] can be equal.
*/
sealed interface ObservableTransitionState {
/**
@@ -49,14 +46,24 @@
}
}
+ /** 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(
- // TODO(b/353679003): Rename these to fromContent and toContent.
- open val fromScene: ContentKey,
- open val toScene: ContentKey,
+ val fromContent: ContentKey,
+ val toContent: ContentKey,
val currentOverlays: Flow<Set<OverlayKey>>,
val progress: Flow<Float>,
@@ -86,8 +93,8 @@
) : ObservableTransitionState {
override fun toString(): String =
"""Transition
- |(from=$fromScene,
- | to=$toScene,
+ |(from=$fromContent,
+ | to=$toContent,
| isInitiatedByUserInput=$isInitiatedByUserInput,
| isUserInputOngoing=$isUserInputOngoing
|)"""
@@ -95,10 +102,10 @@
/** A transition animating between [fromScene] and [toScene]. */
class ChangeScene(
- override val fromScene: SceneKey,
- override val toScene: SceneKey,
+ 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>,
@@ -108,7 +115,7 @@
Transition(
fromScene,
toScene,
- currentOverlays,
+ flowOf(currentOverlays),
progress,
isInitiatedByUserInput,
isUserInputOngoing,
@@ -173,7 +180,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,
@@ -196,8 +203,19 @@
fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean {
return this is Transition &&
- (from == null || this.fromScene == from) &&
- (to == null || this.toScene == to)
+ (from == null || this.fromContent == from) &&
+ (to == null || this.toContent == to)
+ }
+
+ /** Whether we are transitioning from [content] to [other], or from [other] to [content]. */
+ fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean {
+ return isTransitioning(from = content, to = other) ||
+ isTransitioning(from = other, to = content)
+ }
+
+ /** Whether we are transitioning from or to [content]. */
+ fun isTransitioningFromOrTo(content: ContentKey): Boolean {
+ return isTransitioning(from = content) || isTransitioning(to = content)
}
}
@@ -209,13 +227,14 @@
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/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index be4fea1..b00c8ad 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -18,32 +18,38 @@
import androidx.activity.BackEventCompat
import androidx.activity.compose.PredictiveBackHandler
-import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.snap
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
-import kotlin.coroutines.cancellation.CancellationException
+import com.android.compose.animation.scene.UserActionResult.ChangeScene
+import com.android.compose.animation.scene.UserActionResult.HideOverlay
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.transition.animateProgress
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
@Composable
internal fun PredictiveBackHandler(
layoutImpl: SceneTransitionLayoutImpl,
result: UserActionResult?,
) {
- PredictiveBackHandler(
- enabled = result != null,
- ) { progress: Flow<BackEventCompat> ->
+ PredictiveBackHandler(enabled = result != null) { events: Flow<BackEventCompat> ->
if (result == null) {
// Note: We have to collect progress otherwise PredictiveBackHandler will throw.
- progress.first()
+ events.first()
return@PredictiveBackHandler
}
val animation =
createSwipeAnimation(
layoutImpl,
- layoutImpl.coroutineScope,
- result,
+ if (result.transitionKey != null) {
+ result
+ } else {
+ result.copy(transitionKey = TransitionKey.PredictiveBack)
+ },
isUpOrLeft = false,
// Note that the orientation does not matter here given that it's only used to
// compute the distance. In our case the distance is always 1f.
@@ -51,42 +57,30 @@
distance = 1f,
)
- animate(layoutImpl, animation, progress)
+ animateProgress(
+ state = layoutImpl.state,
+ animation = animation,
+ progress = events.map { it.progress },
+
+ // Use the transformationSpec.progressSpec. We will lazily access it later once the
+ // transition has been started, because at this point the transformation spec of the
+ // transition is not computed yet.
+ commitSpec = null,
+
+ // The predictive back APIs will automatically animate the progress for us in this case
+ // so there is no need to animate it.
+ cancelSpec = snap(),
+ )
}
}
-private suspend fun <T : ContentKey> animate(
- layoutImpl: SceneTransitionLayoutImpl,
- animation: SwipeAnimation<T>,
- progress: Flow<BackEventCompat>,
-) {
- fun animateOffset(targetContent: T) {
- if (
- layoutImpl.state.transitionState != animation.contentTransition || animation.isFinishing
- ) {
- return
- }
-
- animation.animateOffset(
- initialVelocity = 0f,
- targetContent = targetContent,
-
- // TODO(b/350705972): Allow to customize or reuse the same customization endpoints as
- // the normal swipe transitions. We can't just reuse them here because other swipe
- // transitions animate pixels while this transition animates progress, so the visibility
- // thresholds will be completely different.
- spec = spring(),
- )
- }
-
- layoutImpl.state.startTransition(animation.contentTransition)
- try {
- progress.collect { backEvent -> animation.dragOffset = backEvent.progress }
-
- // Back gesture successful.
- animateOffset(animation.toContent)
- } catch (e: CancellationException) {
- // Back gesture cancelled.
- animateOffset(animation.fromContent)
+private fun UserActionResult.copy(
+ transitionKey: TransitionKey? = this.transitionKey
+): UserActionResult {
+ return when (this) {
+ is ChangeScene -> copy(transitionKey = transitionKey)
+ is ShowOverlay -> copy(transitionKey = transitionKey)
+ is HideOverlay -> copy(transitionKey = transitionKey)
+ is ReplaceByOverlay -> copy(transitionKey = transitionKey)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index b3f74f7..f20548b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -379,6 +379,10 @@
return this to UserActionResult(toScene = scene)
}
+ infix fun to(overlay: OverlayKey): Pair<UserAction, UserActionResult> {
+ return this to UserActionResult(toOverlay = overlay)
+ }
+
/** Resolve this into a [Resolved] user action given [layoutDirection]. */
internal abstract fun resolve(layoutDirection: LayoutDirection): Resolved
@@ -475,7 +479,7 @@
position: IntOffset,
density: Density,
orientation: Orientation,
- ): SwipeSource?
+ ): SwipeSource.Resolved?
}
/** The result of performing a [UserAction]. */
@@ -503,19 +507,19 @@
}
/** A [UserActionResult] that shows [overlay]. */
- class ShowOverlay(
+ data class ShowOverlay(
val overlay: OverlayKey,
- transitionKey: TransitionKey? = null,
- requiresFullDistanceSwipe: Boolean = false,
+ override val transitionKey: TransitionKey? = null,
+ override val requiresFullDistanceSwipe: Boolean = false,
) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
override fun toContent(currentScene: SceneKey): ContentKey = overlay
}
/** A [UserActionResult] that hides [overlay]. */
- class HideOverlay(
+ data class HideOverlay(
val overlay: OverlayKey,
- transitionKey: TransitionKey? = null,
- requiresFullDistanceSwipe: Boolean = false,
+ override val transitionKey: TransitionKey? = null,
+ override val requiresFullDistanceSwipe: Boolean = false,
) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
override fun toContent(currentScene: SceneKey): ContentKey = currentScene
}
@@ -526,10 +530,10 @@
* Note: This result can only be used for user actions of overlays and an exception will be
* thrown if it is used for a scene.
*/
- class ReplaceByOverlay(
+ data class ReplaceByOverlay(
val overlay: OverlayKey,
- transitionKey: TransitionKey? = null,
- requiresFullDistanceSwipe: Boolean = false,
+ override val transitionKey: TransitionKey? = null,
+ override val requiresFullDistanceSwipe: Boolean = false,
) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
override fun toContent(currentScene: SceneKey): ContentKey = overlay
}
@@ -550,6 +554,22 @@
*/
requiresFullDistanceSwipe: Boolean = false,
): UserActionResult = ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe)
+
+ /** A [UserActionResult] that shows [toOverlay]. */
+ operator fun invoke(
+ /** The overlay we should be transitioning to during the [UserAction]. */
+ toOverlay: OverlayKey,
+
+ /** The key of the transition that should be used. */
+ transitionKey: TransitionKey? = null,
+
+ /**
+ * If `true`, the swipe will be committed if only if the user swiped at least the swipe
+ * distance, i.e. the transition progress was already equal to or bigger than 100% when
+ * the user released their finger.
+ */
+ requiresFullDistanceSwipe: Boolean = false,
+ ): UserActionResult = ShowOverlay(toOverlay, transitionKey, requiresFullDistanceSwipe)
}
}
@@ -565,7 +585,7 @@
*/
fun UserActionDistanceScope.absoluteDistance(
fromSceneSize: IntSize,
- orientation: Orientation
+ orientation: Orientation,
): Float
}
@@ -597,7 +617,7 @@
) {
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
- val coroutineScope = rememberCoroutineScope()
+ val animationScope = rememberCoroutineScope()
val layoutImpl = remember {
SceneTransitionLayoutImpl(
state = state as MutableSceneTransitionLayoutStateImpl,
@@ -606,7 +626,7 @@
swipeSourceDetector = swipeSourceDetector,
transitionInterceptionThreshold = transitionInterceptionThreshold,
builder = builder,
- coroutineScope = coroutineScope,
+ animationScope = animationScope,
)
.also { onLayoutImpl?.invoke(it) }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index b33b4f6..fe05234 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -59,7 +59,13 @@
internal var swipeSourceDetector: SwipeSourceDetector,
internal var transitionInterceptionThreshold: Float,
builder: SceneTransitionLayoutScope.() -> Unit,
- internal val coroutineScope: CoroutineScope,
+
+ /**
+ * The scope that should be used by *animations started by this layout only*, i.e. animations
+ * triggered by gestures set up on this layout in [swipeToScene] or interruption decay
+ * animations.
+ */
+ internal val animationScope: CoroutineScope,
) {
/**
* The map of [Scene]s.
@@ -142,18 +148,10 @@
// DraggableHandlerImpl must wait for the scenes to be initialized, in order to access the
// current scene (required for SwipeTransition).
horizontalDraggableHandler =
- DraggableHandlerImpl(
- layoutImpl = this,
- orientation = Orientation.Horizontal,
- coroutineScope = coroutineScope,
- )
+ DraggableHandlerImpl(layoutImpl = this, orientation = Orientation.Horizontal)
verticalDraggableHandler =
- DraggableHandlerImpl(
- layoutImpl = this,
- orientation = Orientation.Vertical,
- coroutineScope = coroutineScope,
- )
+ DraggableHandlerImpl(layoutImpl = this, orientation = Orientation.Vertical)
// Make sure that the state is created on the same thread (most probably the main thread)
// than this STLImpl.
@@ -255,7 +253,7 @@
key: OverlayKey,
userActions: Map<UserAction, UserActionResult>,
alignment: Alignment,
- content: @Composable (ContentScope.() -> Unit)
+ content: @Composable (ContentScope.() -> Unit),
) {
overlaysDefined = true
overlaysToRemove.remove(key)
@@ -293,7 +291,7 @@
private fun resolveUserActions(
key: ContentKey,
userActions: Map<UserAction, UserActionResult>,
- layoutDirection: LayoutDirection
+ layoutDirection: LayoutDirection,
): Map<UserAction.Resolved, UserActionResult> {
return userActions
.mapKeys { it.key.resolve(layoutDirection) }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index f3128f1..dbff8a4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -30,6 +30,10 @@
import com.android.compose.animation.scene.transition.link.StateLink
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
/**
* The state of a [SceneTransitionLayout].
@@ -108,24 +112,24 @@
* If [targetScene] is different than the [currentScene][TransitionState.currentScene] of
* [transitionState], then this will animate to [targetScene]. The associated
* [TransitionState.Transition] will be returned and will be set as the current
- * [transitionState] of this [MutableSceneTransitionLayoutState].
+ * [transitionState] of this [MutableSceneTransitionLayoutState]. The [Job] in which the
+ * transition runs will be returned, allowing you to easily [join][Job.join] or
+ * [cancel][Job.cancel] the animation.
*
* Note that because a non-null [TransitionState.Transition] is returned does not mean that the
* transition will finish and that we will settle to [targetScene]. The returned transition
* might still be interrupted, for instance by another call to [setTargetScene] or by a user
* gesture.
*
- * If [this] [CoroutineScope] is cancelled during the transition and that the transition was
- * still active, then the [transitionState] of this [MutableSceneTransitionLayoutState] will be
- * set to `TransitionState.Idle(targetScene)`.
- *
- * TODO(b/318794193): Add APIs to await() and cancel() any [TransitionState.Transition].
+ * If [animationScope] is cancelled during the transition and that the transition was still
+ * active, then the [transitionState] of this [MutableSceneTransitionLayoutState] will be set to
+ * `TransitionState.Idle(targetScene)`.
*/
fun setTargetScene(
targetScene: SceneKey,
- coroutineScope: CoroutineScope,
+ animationScope: CoroutineScope,
transitionKey: TransitionKey? = null,
- ): TransitionState.Transition?
+ ): Pair<TransitionState.Transition, Job>?
/** Immediately snap to the given [scene]. */
fun snapToScene(
@@ -297,12 +301,12 @@
override fun setTargetScene(
targetScene: SceneKey,
- coroutineScope: CoroutineScope,
+ animationScope: CoroutineScope,
transitionKey: TransitionKey?,
- ): TransitionState.Transition.ChangeScene? {
+ ): Pair<TransitionState.Transition.ChangeScene, Job>? {
checkThread()
- return coroutineScope.animateToScene(
+ return animationScope.animateToScene(
layoutState = this@MutableSceneTransitionLayoutStateImpl,
target = targetScene,
transitionKey = transitionKey,
@@ -310,17 +314,65 @@
}
/**
+ * Instantly start a [transition], running it in [animationScope].
+ *
+ * This call returns immediately and [transition] will be the [currentTransition] of this
+ * [MutableSceneTransitionLayoutState].
+ *
+ * @see startTransition
+ */
+ internal fun startTransitionImmediately(
+ animationScope: CoroutineScope,
+ transition: TransitionState.Transition,
+ chain: Boolean = true,
+ ): Job {
+ // Note that we start with UNDISPATCHED so that startTransition() is called directly and
+ // transition becomes the current [transitionState] right after this call.
+ return animationScope.launch(start = CoroutineStart.UNDISPATCHED) {
+ startTransition(transition, chain)
+ }
+ }
+
+ /**
* Start a new [transition].
*
* If [chain] is `true`, then the transitions will simply be added to [currentTransitions] and
* will run in parallel to the current transitions. If [chain] is `false`, then the list of
* [currentTransitions] will be cleared and [transition] will be the only running transition.
*
- * Important: you *must* call [finishTransition] once the transition is finished.
+ * If any transition is currently ongoing, it will be interrupted and forced to animate to its
+ * current state.
+ *
+ * This method returns when [transition] is done running, i.e. when the call to
+ * [run][TransitionState.Transition.run] returns.
*/
- internal fun startTransition(transition: TransitionState.Transition, chain: Boolean = true) {
+ internal suspend fun startTransition(
+ transition: TransitionState.Transition,
+ chain: Boolean = true,
+ ) {
checkThread()
+ try {
+ // Keep a reference to the previous transition (if any).
+ val previousTransition = currentTransition
+
+ // Start the transition.
+ startTransitionInternal(transition, chain)
+
+ // Handle transition links.
+ previousTransition?.let { cancelActiveTransitionLinks(it) }
+ if (stateLinks.isNotEmpty()) {
+ coroutineScope { setupTransitionLinks(transition) }
+ }
+
+ // Run the transition until it is finished.
+ transition.run()
+ } finally {
+ finishTransition(transition)
+ }
+ }
+
+ private fun startTransitionInternal(transition: TransitionState.Transition, chain: Boolean) {
// Set the current scene and overlays on the transition.
val currentState = transitionState
transition.currentSceneWhenTransitionStarted = currentState.currentScene
@@ -349,10 +401,6 @@
transition.updateOverscrollSpecs(fromSpec = null, toSpec = null)
}
- // Handle transition links.
- currentTransition?.let { cancelActiveTransitionLinks(it) }
- setupTransitionLinks(transition)
-
if (!enableInterruptions) {
// Set the current transition.
check(transitionStates.size == 1)
@@ -367,9 +415,8 @@
transitionStates = listOf(transition)
}
is TransitionState.Transition -> {
- // Force the current transition to finish to currentScene. The transition will call
- // [finishTransition] once it's finished.
- currentState.finish()
+ // Force the current transition to finish to currentScene.
+ currentState.freezeAndAnimateToCurrentState()
val tooManyTransitions = transitionStates.size >= MAX_CONCURRENT_TRANSITIONS
val clearCurrentTransitions = !chain || tooManyTransitions
@@ -412,7 +459,7 @@
val indicator = if (finishedTransitions.contains(transition)) "x" else " "
appendLine(" [$indicator] $from => $to ($transition)")
}
- }
+ },
)
}
@@ -423,7 +470,7 @@
transition.activeTransitionLinks.clear()
}
- private fun setupTransitionLinks(transition: TransitionState.Transition) {
+ private fun CoroutineScope.setupTransitionLinks(transition: TransitionState.Transition) {
stateLinks.fastForEach { stateLink ->
val matchingLinks =
stateLink.transitionLinks.fastFilter { it.isMatchingLink(transition) }
@@ -443,7 +490,11 @@
key = matchingLink.targetTransitionKey,
)
- stateLink.target.startTransition(linkedTransition)
+ // Start with UNDISPATCHED so that startTransition is called directly and the new linked
+ // transition is observable directly.
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ stateLink.target.startTransition(linkedTransition)
+ }
transition.activeTransitionLinks[stateLink] = linkedTransition
}
}
@@ -453,7 +504,7 @@
* [currentScene][TransitionState.currentScene]. This will do nothing if [transition] was
* interrupted since it was started.
*/
- internal fun finishTransition(transition: TransitionState.Transition) {
+ private fun finishTransition(transition: TransitionState.Transition) {
checkThread()
if (finishedTransitions.contains(transition)) {
@@ -461,6 +512,10 @@
return
}
+ // Make sure that this transition settles in case it was force finished, for instance by
+ // calling snapToScene().
+ transition.freezeAndAnimateToCurrentState()
+
val transitionStates = this.transitionStates
if (!transitionStates.contains(transition)) {
// This transition was already removed from transitionStates.
@@ -564,7 +619,7 @@
override fun showOverlay(
overlay: OverlayKey,
animationScope: CoroutineScope,
- transitionKey: TransitionKey?
+ transitionKey: TransitionKey?,
) {
checkThread()
@@ -597,7 +652,7 @@
) {
animate(
replacedTransition = currentState,
- reversed = overlay == currentState.fromContent
+ reversed = overlay == currentState.fromContent,
)
} else {
animate()
@@ -607,7 +662,7 @@
override fun hideOverlay(
overlay: OverlayKey,
animationScope: CoroutineScope,
- transitionKey: TransitionKey?
+ transitionKey: TransitionKey?,
) {
checkThread()
@@ -648,7 +703,7 @@
from: OverlayKey,
to: OverlayKey,
animationScope: CoroutineScope,
- transitionKey: TransitionKey?
+ transitionKey: TransitionKey?,
) {
checkThread()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index cefcff7..b358faf 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -50,7 +50,7 @@
private val transitionCache =
mutableMapOf<
ContentKey,
- MutableMap<ContentKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
+ MutableMap<ContentKey, MutableMap<TransitionKey?, TransitionSpecImpl>>,
>()
private val overscrollCache =
@@ -70,7 +70,7 @@
private fun findSpec(
from: ContentKey,
to: ContentKey,
- key: TransitionKey?
+ key: TransitionKey?,
): TransitionSpecImpl {
val spec = transition(from, to, key) { it.from == from && it.to == to }
if (spec != null) {
@@ -90,10 +90,19 @@
return relaxedSpec
}
- return transition(from, to, key) {
+ val relaxedReversed =
+ transition(from, to, key) {
(it.from == to && it.to == null) || (it.to == from && it.from == null)
}
- ?.reversed() ?: defaultTransition(from, to)
+ if (relaxedReversed != null) {
+ return relaxedReversed.reversed()
+ }
+
+ return if (key != null) {
+ findSpec(from, to, null)
+ } else {
+ defaultTransition(from, to)
+ }
}
private fun transition(
@@ -241,7 +250,7 @@
override val to: ContentKey?,
private val previewTransformationSpec: (() -> TransformationSpecImpl)? = null,
private val reversePreviewTransformationSpec: (() -> TransformationSpecImpl)? = null,
- private val transformationSpec: () -> TransformationSpecImpl
+ private val transformationSpec: () -> TransformationSpecImpl,
) : TransitionSpec {
override fun reversed(): TransitionSpecImpl {
return TransitionSpecImpl(
@@ -256,9 +265,9 @@
progressSpec = reverse.progressSpec,
swipeSpec = reverse.swipeSpec,
distance = reverse.distance,
- transformations = reverse.transformations.map { it.reversed() }
+ transformations = reverse.transformations.map { it.reversed() },
)
- }
+ },
)
}
@@ -373,11 +382,7 @@
return ElementTransformations(shared, offset, size, drawScale, alpha)
}
- private fun throwIfNotNull(
- previous: Transformation?,
- element: ElementKey,
- name: String,
- ) {
+ private fun throwIfNotNull(previous: Transformation?, element: ElementKey, name: String) {
if (previous != null) {
error("$element has multiple $name transformations")
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 57ff597..84dce0d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -17,8 +17,8 @@
package com.android.compose.animation.scene
import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.SpringSpec
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
@@ -28,14 +28,10 @@
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import kotlin.math.absoluteValue
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.CompletableDeferred
internal fun createSwipeAnimation(
layoutState: MutableSceneTransitionLayoutStateImpl,
- animationScope: CoroutineScope,
result: UserActionResult,
isUpOrLeft: Boolean,
orientation: Orientation,
@@ -43,7 +39,6 @@
): SwipeAnimation<*> {
return createSwipeAnimation(
layoutState,
- animationScope,
result,
isUpOrLeft,
orientation,
@@ -56,11 +51,10 @@
internal fun createSwipeAnimation(
layoutImpl: SceneTransitionLayoutImpl,
- animationScope: CoroutineScope,
result: UserActionResult,
isUpOrLeft: Boolean,
orientation: Orientation,
- distance: Float = DistanceUnspecified
+ distance: Float = DistanceUnspecified,
): SwipeAnimation<*> {
var lastDistance = distance
@@ -88,7 +82,6 @@
return createSwipeAnimation(
layoutImpl.state,
- animationScope,
result,
isUpOrLeft,
orientation,
@@ -99,7 +92,6 @@
private fun createSwipeAnimation(
layoutState: MutableSceneTransitionLayoutStateImpl,
- animationScope: CoroutineScope,
result: UserActionResult,
isUpOrLeft: Boolean,
orientation: Orientation,
@@ -109,7 +101,6 @@
fun <T : ContentKey> swipeAnimation(fromContent: T, toContent: T): SwipeAnimation<T> {
return SwipeAnimation(
layoutState = layoutState,
- animationScope = animationScope,
fromContent = fromContent,
toContent = toContent,
orientation = orientation,
@@ -197,7 +188,6 @@
/** A helper class that contains the main logic for swipe transitions. */
internal class SwipeAnimation<T : ContentKey>(
val layoutState: MutableSceneTransitionLayoutStateImpl,
- val animationScope: CoroutineScope,
val fromContent: T,
val toContent: T,
override val orientation: Orientation,
@@ -210,18 +200,26 @@
/** The [TransitionState.Transition] whose implementation delegates to this [SwipeAnimation]. */
lateinit var contentTransition: TransitionState.Transition
- var currentContent by mutableStateOf(currentContent)
+ private var _currentContent by mutableStateOf(currentContent)
+ var currentContent: T
+ get() = _currentContent
+ set(value) {
+ check(!isAnimatingOffset()) {
+ "currentContent can not be changed once we are animating the offset"
+ }
+ _currentContent = value
+ }
val progress: Float
get() {
// Important: If we are going to return early because distance is equal to 0, we should
// still make sure we read the offset before returning so that the calling code still
// subscribes to the offset value.
- val animatable = offsetAnimation?.animatable
+ val animatable = offsetAnimation
val offset =
when {
+ isInPreviewStage -> 0f
animatable != null -> animatable.value
- contentTransition.previewTransformationSpec != null -> 0f
else -> dragOffset
}
@@ -238,7 +236,7 @@
val progressVelocity: Float
get() {
- val animatable = offsetAnimation?.animatable ?: return 0f
+ val animatable = offsetAnimation ?: return 0f
val distance = distance()
if (distance == DistanceUnspecified) {
return 0f
@@ -249,7 +247,15 @@
}
val previewProgress: Float
- get() = computeProgress(dragOffset)
+ get() {
+ val offset =
+ if (isInPreviewStage) {
+ offsetAnimation?.value ?: dragOffset
+ } else {
+ dragOffset
+ }
+ return computeProgress(offset)
+ }
val previewProgressVelocity: Float
get() = 0f
@@ -263,7 +269,8 @@
var dragOffset by mutableFloatStateOf(dragOffset)
/** The offset animation that animates the offset once the user lifts their finger. */
- private var offsetAnimation: OffsetAnimation? by mutableStateOf(null)
+ private var offsetAnimation: Animatable<Float, AnimationVector1D>? by mutableStateOf(null)
+ private val offsetAnimationRunnable = CompletableDeferred<(suspend () -> Unit)?>()
val isUserInputOngoing: Boolean
get() = offsetAnimation == null
@@ -271,15 +278,10 @@
override val absoluteDistance: Float
get() = distance().absoluteValue
- /** Whether [finish] was called on this animation. */
- var isFinishing = false
- private set
-
constructor(
other: SwipeAnimation<T>
) : this(
layoutState = other.layoutState,
- animationScope = other.animationScope,
fromContent = other.fromContent,
toContent = other.toContent,
orientation = other.orientation,
@@ -287,9 +289,17 @@
requiresFullDistanceSwipe = other.requiresFullDistanceSwipe,
distance = other.distance,
currentContent = other.currentContent,
- dragOffset = other.dragOffset,
+ dragOffset = other.offsetAnimation?.value ?: other.dragOffset,
)
+ suspend fun run() {
+ // This animation will first be driven by finger, then when the user lift their finger we
+ // start an animation to the target offset (progress = 1f or progress = 0f). We await() for
+ // offsetAnimationRunnable to be completed and then run it.
+ val runAnimation = offsetAnimationRunnable.await() ?: return
+ runAnimation()
+ }
+
/**
* The signed distance between [fromContent] and [toContent]. It is negative if [fromContent] is
* above or to the left of [toContent].
@@ -300,28 +310,20 @@
*/
fun distance(): Float = distance(this)
- /** Ends any previous [offsetAnimation] and runs the new [animation]. */
- private fun startOffsetAnimation(animation: () -> OffsetAnimation): OffsetAnimation {
- cancelOffsetAnimation()
- return animation().also { offsetAnimation = it }
- }
+ fun isAnimatingOffset(): Boolean = offsetAnimation != null
- /** Cancel any ongoing offset animation. */
- // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at
- // the same time.
- fun cancelOffsetAnimation() {
- val animation = offsetAnimation ?: return
- offsetAnimation = null
-
- dragOffset = animation.animatable.value
- animation.job.cancel()
- }
-
+ /**
+ * Animate the offset to a [targetContent], using the [initialVelocity] and an optional [spec]
+ *
+ * @return the velocity consumed
+ */
fun animateOffset(
initialVelocity: Float,
targetContent: T,
- spec: SpringSpec<Float>? = null,
- ): OffsetAnimation {
+ spec: AnimationSpec<Float>? = null,
+ ): Float {
+ check(!isAnimatingOffset()) { "SwipeAnimation.animateOffset() can only be called once" }
+
val initialProgress = progress
// Skip the animation if we have already reached the target content and the overscroll does
// not animate anything.
@@ -358,74 +360,83 @@
currentContent = targetContent
}
- return startOffsetAnimation {
- val startProgress =
- if (contentTransition.previewTransformationSpec != null) 0f else dragOffset
- val animatable = Animatable(startProgress, OffsetVisibilityThreshold)
- val isTargetGreater = targetOffset > animatable.value
- val startedWhenOvercrollingTargetContent =
- if (targetContent == fromContent) initialProgress < 0f else initialProgress > 1f
- val job =
- animationScope
- // Important: We start atomically to make sure that we start the coroutine even
- // if it is cancelled right after it is launched, so that snapToContent() is
- // correctly called. Otherwise, this transition will never be stopped and we
- // will never settle to Idle.
- .launch(start = CoroutineStart.ATOMIC) {
- // TODO(b/327249191): Refactor the code so that we don't even launch a
- // coroutine if we don't need to animate.
- if (skipAnimation) {
- snapToContent(targetContent)
- dragOffset = targetOffset
- return@launch
- }
+ val startProgress =
+ if (contentTransition.previewTransformationSpec != null && targetContent == toContent) {
+ 0f
+ } else {
+ dragOffset
+ }
- try {
- val swipeSpec =
- spec
- ?: contentTransition.transformationSpec.swipeSpec
- ?: layoutState.transitions.defaultSwipeSpec
- animatable.animateTo(
- targetValue = targetOffset,
- animationSpec = swipeSpec,
- initialVelocity = initialVelocity,
- ) {
- if (bouncingContent == null) {
- val isBouncing =
- if (isTargetGreater) {
- if (startedWhenOvercrollingTargetContent) {
- value >= targetOffset
- } else {
- value > targetOffset
- }
- } else {
- if (startedWhenOvercrollingTargetContent) {
- value <= targetOffset
- } else {
- value < targetOffset
- }
- }
+ val animatable =
+ Animatable(startProgress, OffsetVisibilityThreshold).also { offsetAnimation = it }
- if (isBouncing) {
- bouncingContent = targetContent
+ check(isAnimatingOffset())
- // Immediately stop this transition if we are bouncing on a
- // content that does not bounce.
- if (!contentTransition.isWithinProgressRange(progress)) {
- snapToContent(targetContent)
- }
- }
+ // Note: we still create the animatable and set it on offsetAnimation even when
+ // skipAnimation is true, just so that isUserInputOngoing and isAnimatingOffset() are
+ // unchanged even despite this small skip-optimization (which is just an implementation
+ // detail).
+ if (skipAnimation) {
+ // Unblock the job.
+ offsetAnimationRunnable.complete(null)
+ return 0f
+ }
+
+ val isTargetGreater = targetOffset > animatable.value
+ val startedWhenOvercrollingTargetContent =
+ if (targetContent == fromContent) initialProgress < 0f else initialProgress > 1f
+
+ val swipeSpec =
+ spec
+ ?: contentTransition.transformationSpec.swipeSpec
+ ?: layoutState.transitions.defaultSwipeSpec
+
+ offsetAnimationRunnable.complete {
+ try {
+ animatable.animateTo(
+ targetValue = targetOffset,
+ animationSpec = swipeSpec,
+ initialVelocity = initialVelocity,
+ ) {
+ if (bouncingContent == null) {
+ val isBouncing =
+ if (isTargetGreater) {
+ if (startedWhenOvercrollingTargetContent) {
+ value >= targetOffset
+ } else {
+ value > targetOffset
+ }
+ } else {
+ if (startedWhenOvercrollingTargetContent) {
+ value <= targetOffset
+ } else {
+ value < targetOffset
}
}
- } finally {
- snapToContent(targetContent)
+
+ if (isBouncing) {
+ bouncingContent = targetContent
+
+ // Immediately stop this transition if we are bouncing on a content that
+ // does not bounce.
+ if (!contentTransition.isWithinProgressRange(progress)) {
+ throw SnapException()
+ }
}
}
-
- OffsetAnimation(animatable, job)
+ }
+ } catch (_: SnapException) {
+ /* Ignore. */
+ }
}
+
+ // This animation always consumes the whole available velocity
+ return initialVelocity
}
+ /** An exception thrown during the animation to stop it immediately. */
+ private class SnapException : Exception()
+
private fun canChangeContent(targetContent: ContentKey): Boolean {
return when (val transition = contentTransition) {
is TransitionState.Transition.ChangeScene ->
@@ -446,34 +457,11 @@
}
}
- private fun snapToContent(content: T) {
- cancelOffsetAnimation()
- check(currentContent == content)
- layoutState.finishTransition(contentTransition)
+ fun freezeAndAnimateToCurrentState() {
+ if (isAnimatingOffset()) return
+
+ animateOffset(initialVelocity = 0f, targetContent = currentContent)
}
-
- fun finish(): Job {
- if (isFinishing) return requireNotNull(offsetAnimation).job
- isFinishing = true
-
- // If we were already animating the offset, simply return the job.
- offsetAnimation?.let {
- return it.job
- }
-
- // Animate to the current content.
- val animation = animateOffset(initialVelocity = 0f, targetContent = currentContent)
- check(offsetAnimation == animation)
- return animation.job
- }
-
- internal class OffsetAnimation(
- /** The animatable used to animate the offset. */
- val animatable: Animatable<Float, AnimationVector1D>,
-
- /** The job in which [animatable] is animated. */
- val job: Job,
- )
}
private object DefaultSwipeDistance : UserActionDistance {
@@ -537,7 +525,13 @@
override val isUserInputOngoing: Boolean
get() = swipeAnimation.isUserInputOngoing
- override fun finish(): Job = swipeAnimation.finish()
+ override suspend fun run() {
+ swipeAnimation.run()
+ }
+
+ override fun freezeAndAnimateToCurrentState() {
+ swipeAnimation.freezeAndAnimateToCurrentState()
+ }
}
private class ShowOrHideOverlaySwipeTransition(
@@ -594,7 +588,13 @@
override val isUserInputOngoing: Boolean
get() = swipeAnimation.isUserInputOngoing
- override fun finish(): Job = swipeAnimation.finish()
+ override suspend fun run() {
+ swipeAnimation.run()
+ }
+
+ override fun freezeAndAnimateToCurrentState() {
+ swipeAnimation.freezeAndAnimateToCurrentState()
+ }
}
private class ReplaceOverlaySwipeTransition(
@@ -645,5 +645,11 @@
override val isUserInputOngoing: Boolean
get() = swipeAnimation.isUserInputOngoing
- override fun finish(): Job = swipeAnimation.finish()
+ override suspend fun run() {
+ swipeAnimation.run()
+ }
+
+ override fun freezeAndAnimateToCurrentState() {
+ swipeAnimation.freezeAndAnimateToCurrentState()
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index dc7eda5..98d4aaa 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -39,14 +39,14 @@
@Stable
internal fun Modifier.swipeToScene(
draggableHandler: DraggableHandlerImpl,
- swipeDetector: SwipeDetector
+ swipeDetector: SwipeDetector,
): Modifier {
return this.then(SwipeToSceneElement(draggableHandler, swipeDetector))
}
private data class SwipeToSceneElement(
val draggableHandler: DraggableHandlerImpl,
- val swipeDetector: SwipeDetector
+ val swipeDetector: SwipeDetector,
) : ModifierNodeElement<SwipeToSceneNode>() {
override fun create(): SwipeToSceneNode = SwipeToSceneNode(draggableHandler, swipeDetector)
@@ -183,12 +183,12 @@
*/
private class ScrollBehaviorOwnerNode(
override val traverseKey: Any,
- val nestedScrollHandlerImpl: NestedScrollHandlerImpl
+ val nestedScrollHandlerImpl: NestedScrollHandlerImpl,
) : Modifier.Node(), TraversableNode, ScrollBehaviorOwner {
override fun updateScrollBehaviors(
topOrLeftBehavior: NestedScrollBehavior,
bottomOrRightBehavior: NestedScrollBehavior,
- isExternalOverscrollGesture: () -> Boolean
+ isExternalOverscrollGesture: () -> Boolean,
) {
nestedScrollHandlerImpl.topOrLeftBehavior = topOrLeftBehavior
nestedScrollHandlerImpl.bottomOrRightBehavior = bottomOrRightBehavior
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 2b5953c..763dc6b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -104,23 +104,23 @@
): TransitionSpec
/**
- * Define the animation to be played when the [scene] is overscrolled in the given
+ * Define the animation to be played when the [content] is overscrolled in the given
* [orientation].
*
* The overscroll animation always starts from a progress of 0f, and reaches 1f when moving the
* [distance] down/right, -1f when moving in the opposite direction.
*/
fun overscroll(
- scene: SceneKey,
+ content: ContentKey,
orientation: Orientation,
builder: OverscrollBuilder.() -> Unit,
): OverscrollSpec
/**
- * Prevents overscroll the [scene] in the given [orientation], allowing ancestors to eventually
- * consume the remaining gesture.
+ * Prevents overscroll the [content] in the given [orientation], allowing ancestors to
+ * eventually consume the remaining gesture.
*/
- fun overscrollDisabled(scene: SceneKey, orientation: Orientation): OverscrollSpec
+ fun overscrollDisabled(content: ContentKey, orientation: Orientation): OverscrollSpec
}
interface BaseTransitionBuilder : PropertyTransformationBuilder {
@@ -333,7 +333,7 @@
element: ElementKey,
transition: TransitionState.Transition,
fromContentZIndex: Float,
- toContentZIndex: Float
+ toContentZIndex: Float,
): ContentKey {
return if (fromContentZIndex > toContentZIndex) {
transition.fromContent
@@ -354,7 +354,7 @@
element: ElementKey,
transition: TransitionState.Transition,
fromContentZIndex: Float,
- toContentZIndex: Float
+ toContentZIndex: Float,
): ContentKey {
return HighestZIndexContentPicker.contentDuringTransition(
element,
@@ -375,7 +375,7 @@
element: ElementKey,
transition: TransitionState.Transition,
fromContentZIndex: Float,
- toContentZIndex: Float
+ toContentZIndex: Float,
): ContentKey {
return if (fromContentZIndex < toContentZIndex) {
transition.fromContent
@@ -396,7 +396,7 @@
element: ElementKey,
transition: TransitionState.Transition,
fromContentZIndex: Float,
- toContentZIndex: Float
+ toContentZIndex: Float,
): ContentKey {
return LowestZIndexContentPicker.contentDuringTransition(
element,
@@ -423,9 +423,8 @@
* is not the same as when going from scene B to scene A, so it's not usable in situations where
* z-ordering during the transition matters.
*/
-class MovableElementContentPicker(
- override val contents: Set<ContentKey>,
-) : StaticElementContentPicker {
+class MovableElementContentPicker(override val contents: Set<ContentKey>) :
+ StaticElementContentPicker {
override fun contentDuringTransition(
element: ElementKey,
transition: TransitionState.Transition,
@@ -501,7 +500,7 @@
matcher: ElementMatcher,
scaleX: Float = 1f,
scaleY: Float = 1f,
- pivot: Offset = Offset.Unspecified
+ pivot: Offset = Offset.Unspecified,
)
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 18e356f..7ec5e4f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -41,9 +41,7 @@
import com.android.compose.animation.scene.transformation.TransformationRange
import com.android.compose.animation.scene.transformation.Translate
-internal fun transitionsImpl(
- builder: SceneTransitionsBuilder.() -> Unit,
-): SceneTransitions {
+internal fun transitionsImpl(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
val impl = SceneTransitionsBuilderImpl().apply(builder)
return SceneTransitions(
impl.defaultSwipeSpec,
@@ -67,7 +65,7 @@
key: TransitionKey?,
preview: (TransitionBuilder.() -> Unit)?,
reversePreview: (TransitionBuilder.() -> Unit)?,
- builder: TransitionBuilder.() -> Unit
+ builder: TransitionBuilder.() -> Unit,
): TransitionSpec {
return transition(from = null, to = to, key = key, preview, reversePreview, builder)
}
@@ -78,36 +76,36 @@
key: TransitionKey?,
preview: (TransitionBuilder.() -> Unit)?,
reversePreview: (TransitionBuilder.() -> Unit)?,
- builder: TransitionBuilder.() -> Unit
+ builder: TransitionBuilder.() -> Unit,
): TransitionSpec {
return transition(from = from, to = to, key = key, preview, reversePreview, builder)
}
override fun overscroll(
- scene: SceneKey,
+ content: ContentKey,
orientation: Orientation,
- builder: OverscrollBuilder.() -> Unit
+ builder: OverscrollBuilder.() -> Unit,
): OverscrollSpec {
val impl = OverscrollBuilderImpl().apply(builder)
check(impl.transformations.isNotEmpty()) {
"This method does not allow empty transformations. " +
- "Use overscrollDisabled($scene, $orientation) instead."
+ "Use overscrollDisabled($content, $orientation) instead."
}
- return overscrollSpec(scene, orientation, impl)
+ return overscrollSpec(content, orientation, impl)
}
- override fun overscrollDisabled(scene: SceneKey, orientation: Orientation): OverscrollSpec {
- return overscrollSpec(scene, orientation, OverscrollBuilderImpl())
+ override fun overscrollDisabled(content: ContentKey, orientation: Orientation): OverscrollSpec {
+ return overscrollSpec(content, orientation, OverscrollBuilderImpl())
}
private fun overscrollSpec(
- scene: SceneKey,
+ content: ContentKey,
orientation: Orientation,
impl: OverscrollBuilderImpl,
): OverscrollSpec {
val spec =
OverscrollSpecImpl(
- content = scene,
+ content = content,
orientation = orientation,
transformationSpec =
TransformationSpecImpl(
@@ -150,7 +148,7 @@
to,
previewTransformationSpec,
reversePreviewTransformationSpec,
- transformationSpec
+ transformationSpec,
)
transitionSpecs.add(spec)
return spec
@@ -167,7 +165,7 @@
start: Float?,
end: Float?,
easing: Easing,
- builder: PropertyTransformationBuilder.() -> Unit
+ builder: PropertyTransformationBuilder.() -> Unit,
) {
range = TransformationRange(start, end, easing)
builder()
@@ -202,7 +200,7 @@
override fun translate(
matcher: ElementMatcher,
edge: Edge,
- startsOutsideLayoutBounds: Boolean
+ startsOutsideLayoutBounds: Boolean,
) {
transformation(EdgeTranslate(matcher, edge, startsOutsideLayoutBounds))
}
@@ -256,7 +254,7 @@
startMillis: Int?,
endMillis: Int?,
easing: Easing,
- builder: PropertyTransformationBuilder.() -> Unit
+ builder: PropertyTransformationBuilder.() -> Unit,
) {
if (startMillis != null && (startMillis < 0 || startMillis > durationMillis)) {
error("invalid start value: startMillis=$startMillis durationMillis=$durationMillis")
@@ -278,7 +276,7 @@
override fun translate(
matcher: ElementMatcher,
x: OverscrollScope.() -> Float,
- y: OverscrollScope.() -> Float
+ y: OverscrollScope.() -> Float,
) {
transformation(OverscrollTranslate(matcher, x, y))
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index 9851b32..b7fa0d4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -19,9 +19,8 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntSize
-internal class ElementStateScopeImpl(
- private val layoutImpl: SceneTransitionLayoutImpl,
-) : ElementStateScope {
+internal class ElementStateScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) :
+ ElementStateScope {
override fun ElementKey.targetSize(scene: SceneKey): IntSize? {
return layoutImpl.elements[this]?.stateByContent?.get(scene)?.targetSize.takeIf {
it != Element.SizeUnspecified
@@ -39,9 +38,8 @@
}
}
-internal class UserActionDistanceScopeImpl(
- private val layoutImpl: SceneTransitionLayoutImpl,
-) : UserActionDistanceScope, ElementStateScope by layoutImpl.elementStateScope {
+internal class UserActionDistanceScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) :
+ UserActionDistanceScope, ElementStateScope by layoutImpl.elementStateScope {
override val density: Float
get() = layoutImpl.density.density
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 59dd896..c8407b1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -106,7 +106,7 @@
override fun Element(
key: ElementKey,
modifier: Modifier,
- content: @Composable (ElementScope<ElementContentScope>.() -> Unit)
+ content: @Composable (ElementScope<ElementContentScope>.() -> Unit),
) {
Element(layoutImpl, [email protected], key, modifier, content)
}
@@ -115,7 +115,7 @@
override fun MovableElement(
key: MovableElementKey,
modifier: Modifier,
- content: @Composable (ElementScope<MovableElementContentScope>.() -> Unit)
+ content: @Composable (ElementScope<MovableElementContentScope>.() -> Unit),
) {
MovableElement(layoutImpl, [email protected], key, modifier, content)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
index 0cd8c1a..364c203 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -35,7 +35,6 @@
import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.transition.link.LinkedTransition
import com.android.compose.animation.scene.transition.link.StateLink
-import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
/** The state associated to a [SceneTransitionLayout] at some specific point in time. */
@@ -185,7 +184,7 @@
private fun computeCurrentOverlays(
include: OverlayKey,
- exclude: OverlayKey
+ exclude: OverlayKey,
): Set<OverlayKey> {
return buildSet {
addAll(currentOverlaysWhenTransitionStarted)
@@ -300,19 +299,19 @@
return fromContent == content || toContent == content
}
+ /** Run this transition and return once it is finished. */
+ internal abstract suspend fun run()
+
/**
- * Force this transition to finish and animate to an [Idle] state.
+ * Freeze this transition state so that neither [currentScene] nor [currentOverlays] will
+ * change in the future, and animate the progress towards that state. For instance, a
+ * [Transition.ChangeScene] should animate the progress to 0f if its [currentScene] is equal
+ * to its [fromScene][Transition.ChangeScene.fromScene] or animate it to 1f if its equal to
+ * its [toScene][Transition.ChangeScene.toScene].
*
- * Important: Once this is called, the effective state of the transition should remain
- * unchanged. For instance, in the case of a [TransitionState.Transition], its
- * [currentScene][TransitionState.Transition.currentScene] should never change once [finish]
- * is called.
- *
- * @return the [Job] that animates to the idle state. It can be used to wait until the
- * animation is complete or cancel it to snap the animation. Calling [finish] multiple
- * times will return the same [Job].
+ * This is called when this transition is interrupted (replaced) by another transition.
*/
- internal abstract fun finish(): Job
+ internal abstract fun freezeAndAnimateToCurrentState()
internal fun updateOverscrollSpecs(
fromSpec: OverscrollSpecImpl?,
@@ -337,9 +336,7 @@
return specForCurrentScene.transformationSpec.transformations.isNotEmpty()
}
- internal open fun interruptionProgress(
- layoutImpl: SceneTransitionLayoutImpl,
- ): Float {
+ internal open fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float {
if (!layoutImpl.state.enableInterruptions) {
return 0f
}
@@ -350,7 +347,7 @@
fun create(): Animatable<Float, AnimationVector1D> {
val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold)
- layoutImpl.coroutineScope.launch {
+ layoutImpl.animationScope.launch {
val swipeSpec = layoutImpl.state.transitions.defaultSwipeSpec
val progressSpec =
spring(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt
index a4bd2be..4698e58 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt
@@ -119,9 +119,8 @@
return this.then(SizeMatcherDestinationElement(matcher))
}
-private data class SizeMatcherSourceNodeElement(
- private val matcher: SizeMatcher,
-) : ModifierNodeElement<SizeMatcherSourceNode>() {
+private data class SizeMatcherSourceNodeElement(private val matcher: SizeMatcher) :
+ ModifierNodeElement<SizeMatcherSourceNode>() {
override fun create(): SizeMatcherSourceNode = SizeMatcherSourceNode(matcher)
override fun update(node: SizeMatcherSourceNode) {
@@ -129,9 +128,8 @@
}
}
-private class SizeMatcherSourceNode(
- private var matcher: SizeMatcher,
-) : Modifier.Node(), LayoutModifierNode {
+private class SizeMatcherSourceNode(private var matcher: SizeMatcher) :
+ Modifier.Node(), LayoutModifierNode {
override fun onAttach() {
matcher.source = this
}
@@ -150,7 +148,7 @@
override fun MeasureScope.measure(
measurable: Measurable,
- constraints: Constraints
+ constraints: Constraints,
): MeasureResult {
return measurable.measure(constraints).run {
matcher.sourceSize = IntSize(width, height)
@@ -159,9 +157,8 @@
}
}
-private data class SizeMatcherDestinationElement(
- private val matcher: SizeMatcher,
-) : ModifierNodeElement<SizeMatcherDestinationNode>() {
+private data class SizeMatcherDestinationElement(private val matcher: SizeMatcher) :
+ ModifierNodeElement<SizeMatcherDestinationNode>() {
override fun create(): SizeMatcherDestinationNode = SizeMatcherDestinationNode(matcher)
override fun update(node: SizeMatcherDestinationNode) {
@@ -169,9 +166,8 @@
}
}
-private class SizeMatcherDestinationNode(
- private var matcher: SizeMatcher,
-) : Modifier.Node(), LayoutModifierNode {
+private class SizeMatcherDestinationNode(private var matcher: SizeMatcher) :
+ Modifier.Node(), LayoutModifierNode {
override fun onAttach() {
this.matcher.destinations.add(this)
}
@@ -190,7 +186,7 @@
override fun MeasureScope.measure(
measurable: Measurable,
- constraints: Constraints
+ constraints: Constraints,
): MeasureResult {
val preferredSize = matcher.sourceSize
val preferredConstraints = Constraints.fixed(preferredSize.width, preferredSize.height)
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/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 05878c2..86e06ab 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -61,15 +61,9 @@
val offset = anchorToOffset - anchorFromOffset
return if (content == transition.toContent) {
- Offset(
- value.x - offset.x,
- value.y - offset.y,
- )
+ Offset(value.x - offset.x, value.y - offset.y)
} else {
- Offset(
- value.x + offset.x,
- value.y + offset.y,
- )
+ Offset(value.x + offset.x, value.y + offset.y)
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index a32c7dd..031f50e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -36,7 +36,7 @@
element: Element,
stateInContent: Element.State,
transition: TransitionState.Transition,
- value: Offset
+ value: Offset,
): Offset {
val sceneSize = layoutImpl.content(content).targetSize
val elementSize = stateInContent.targetSize
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index 4528eef..078aa0f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -23,16 +23,14 @@
import com.android.compose.animation.scene.content.state.TransitionState
/** Fade an element in or out. */
-internal class Fade(
- override val matcher: ElementMatcher,
-) : PropertyTransformation<Float> {
+internal class Fade(override val matcher: ElementMatcher) : PropertyTransformation<Float> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: Element,
stateInContent: Element.State,
transition: TransitionState.Transition,
- value: Float
+ value: Float,
): Float {
// Return the alpha value of [element] either when it starts fading in or when it finished
// fading out, which is `0` in both cases.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 505ad04..9bb3023 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -83,17 +83,13 @@
override fun reversed(): Transformation {
return RangedPropertyTransformation(
delegate.reversed() as PropertyTransformation<T>,
- range.reversed()
+ range.reversed(),
)
}
}
/** The progress-based range of a [PropertyTransformation]. */
-data class TransformationRange(
- val start: Float,
- val end: Float,
- val easing: Easing,
-) {
+data class TransformationRange(val start: Float, val end: Float, val easing: Easing) {
constructor(
start: Float? = null,
end: Float? = null,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index 8f84586..7014271 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -40,12 +40,7 @@
transition: TransitionState.Transition,
value: Offset,
): Offset {
- return with(layoutImpl.density) {
- Offset(
- value.x + x.toPx(),
- value.y + y.toPx(),
- )
- }
+ return with(layoutImpl.density) { Offset(value.x + x.toPx(), value.y + y.toPx()) }
}
}
@@ -71,10 +66,7 @@
val overscrollScope =
cachedOverscrollScope.getFromCacheOrCompute(layoutImpl.density, overscrollProperties)
- return Offset(
- x = value.x + overscrollScope.x(),
- y = value.y + overscrollScope.y(),
- )
+ return Offset(x = value.x + overscrollScope.x(), y = value.y + overscrollScope.y())
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
new file mode 100644
index 0000000..715d979
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
@@ -0,0 +1,187 @@
+/*
+ * 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.transition
+
+import androidx.annotation.FloatRange
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.util.fastCoerceIn
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SwipeAnimation
+import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.createSwipeAnimation
+import kotlin.coroutines.cancellation.CancellationException
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+/**
+ * Seek to the given [scene] using [progress].
+ *
+ * This will start a transition from the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene] to [scene], driven by the
+ * progress in [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToScene(
+ scene: SceneKey,
+ @FloatRange(0.0, 1.0) progress: Flow<Float>,
+ transitionKey: TransitionKey? = null,
+ animationSpec: AnimationSpec<Float>? = null,
+) {
+ require(scene != currentScene) {
+ "seekToScene($scene) has to be called with a different scene than the current scene"
+ }
+
+ seek(UserActionResult.ChangeScene(scene, transitionKey), progress, animationSpec)
+}
+
+/**
+ * Seek to show the given [overlay] using [progress].
+ *
+ * This will start a transition to show [overlay] from the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene], driven by the progress in
+ * [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToShowOverlay(
+ overlay: OverlayKey,
+ @FloatRange(0.0, 1.0) progress: Flow<Float>,
+ transitionKey: TransitionKey? = null,
+ animationSpec: AnimationSpec<Float>? = null,
+) {
+ require(overlay in currentOverlays) {
+ "seekToShowOverlay($overlay) can be called only when the overlay is in currentOverlays"
+ }
+
+ seek(UserActionResult.ShowOverlay(overlay, transitionKey), progress, animationSpec)
+}
+
+/**
+ * Seek to hide the given [overlay] using [progress].
+ *
+ * This will start a transition to hide [overlay] to the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene], driven by the progress in
+ * [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToHideOverlay(
+ overlay: OverlayKey,
+ @FloatRange(0.0, 1.0) progress: Flow<Float>,
+ transitionKey: TransitionKey? = null,
+ animationSpec: AnimationSpec<Float>? = null,
+) {
+ require(overlay !in currentOverlays) {
+ "seekToHideOverlay($overlay) can be called only when the overlay is *not* in " +
+ "currentOverlays"
+ }
+
+ seek(UserActionResult.HideOverlay(overlay, transitionKey), progress, animationSpec)
+}
+
+private suspend fun MutableSceneTransitionLayoutState.seek(
+ result: UserActionResult,
+ progress: Flow<Float>,
+ animationSpec: AnimationSpec<Float>?,
+) {
+ val layoutState =
+ when (this) {
+ is MutableSceneTransitionLayoutStateImpl -> this
+ }
+
+ val swipeAnimation =
+ createSwipeAnimation(
+ layoutState = layoutState,
+ result = result,
+
+ // We are animating progress, so distance is always 1f.
+ distance = 1f,
+
+ // The orientation and isUpOrLeft don't matter here given that they are only used during
+ // overscroll, which is disabled for progress-based transitions.
+ orientation = Orientation.Horizontal,
+ isUpOrLeft = false,
+ )
+
+ animateProgress(
+ state = layoutState,
+ animation = swipeAnimation,
+ progress = progress,
+ commitSpec = animationSpec,
+ cancelSpec = animationSpec,
+ )
+}
+
+internal suspend fun <T : ContentKey> animateProgress(
+ state: MutableSceneTransitionLayoutStateImpl,
+ animation: SwipeAnimation<T>,
+ progress: Flow<Float>,
+ commitSpec: AnimationSpec<Float>?,
+ cancelSpec: AnimationSpec<Float>?,
+) {
+ fun animateOffset(targetContent: T, spec: AnimationSpec<Float>?) {
+ if (state.transitionState != animation.contentTransition || animation.isAnimatingOffset()) {
+ return
+ }
+
+ animation.animateOffset(
+ initialVelocity = 0f,
+ targetContent = targetContent,
+
+ // Important: we have to specify a spec that correctly animates *progress* (low
+ // visibility threshold) and not *offset* (higher visibility threshold).
+ spec = spec ?: animation.contentTransition.transformationSpec.progressSpec,
+ )
+ }
+
+ coroutineScope {
+ val collectionJob = launch {
+ try {
+ progress.collectLatest { progress ->
+ // Progress based animation should never overscroll given that the
+ // absoluteDistance exposed to overscroll builders is always 1f and will not
+ // lead to any noticeable transformation.
+ animation.dragOffset = progress.fastCoerceIn(0f, 1f)
+ }
+
+ // Transition committed.
+ animateOffset(animation.toContent, commitSpec)
+ } catch (e: CancellationException) {
+ // Transition cancelled.
+ animateOffset(animation.fromContent, cancelSpec)
+ }
+ }
+
+ // Start the transition.
+ state.startTransition(animation.contentTransition)
+
+ // The transition is done. Cancel the collection in case the transition was finished because
+ // it was interrupted by another transition.
+ if (collectionJob.isActive) {
+ collectionJob.cancel()
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
index 564d4b3..42ba9ba 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
@@ -19,7 +19,6 @@
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.content.state.TransitionState
-import kotlinx.coroutines.Job
/** A linked transition which is driven by a [originalTransition]. */
internal class LinkedTransition(
@@ -50,5 +49,11 @@
override val progressVelocity: Float
get() = originalTransition.progressVelocity
- override fun finish(): Job = originalTransition.finish()
+ override suspend fun run() {
+ originalTransition.run()
+ }
+
+ override fun freezeAndAnimateToCurrentState() {
+ originalTransition.freezeAndAnimateToCurrentState()
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
index c830ca4..2aec509 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
@@ -50,9 +50,7 @@
error("From and To can't be the same")
}
- internal fun isMatchingLink(
- transition: TransitionState.Transition,
- ): Boolean {
+ internal fun isMatchingLink(transition: TransitionState.Transition): Boolean {
return (sourceFrom == null || sourceFrom == transition.fromContent) &&
(sourceTo == null || sourceTo == transition.toContent)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
index 790665a..f49939b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
@@ -99,10 +99,7 @@
}
}
- Layout(
- modifier = modifier,
- content = content,
- ) { measurables, constraints ->
+ Layout(modifier = modifier, content = content) { measurables, constraints ->
val cells = measurables.size
val columns: Int
val rows: Int
@@ -142,7 +139,7 @@
(constraints.maxHeight - totalVerticalSpacingBetweenChildren) / rows
} else {
Constraints.Infinity
- }
+ },
)
val placeables = buildList {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
index e78ab29..0447c36 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
@@ -27,7 +27,7 @@
fun lerp(start: IntSize, stop: IntSize, fraction: Float): IntSize {
return IntSize(
lerp(start.width, stop.width, fraction),
- lerp(start.height, stop.height, fraction)
+ lerp(start.height, stop.height, fraction),
)
}
@@ -43,6 +43,6 @@
return Scale(
lerp(start.scaleX, stop.scaleX, fraction),
lerp(start.scaleY, stop.scaleY, fraction),
- pivot
+ pivot,
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
index a13e944..f08a180 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
@@ -22,8 +22,11 @@
interface SpaceVectorConverter {
fun Offset.toFloat(): Float
+
fun Velocity.toFloat(): Float
+
fun Float.toOffset(): Offset
+
fun Float.toVelocity(): Velocity
}
@@ -36,15 +39,21 @@
private val HorizontalConverter =
object : SpaceVectorConverter {
override fun Offset.toFloat() = x
+
override fun Velocity.toFloat() = x
+
override fun Float.toOffset() = Offset(this, 0f)
+
override fun Float.toVelocity() = Velocity(this, 0f)
}
private val VerticalConverter =
object : SpaceVectorConverter {
override fun Offset.toFloat() = y
+
override fun Velocity.toFloat() = y
+
override fun Float.toOffset() = Offset(0f, this)
+
override fun Float.toVelocity() = Velocity(0f, this)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt
index 3fda9b8..00e5405 100644
--- a/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt
@@ -33,7 +33,7 @@
* SwipeSourceDetector} to enable fullscreen swipe handling to transition to and from the glanceable
* hub.
*/
-class CommunalSwipeDetector(private var lastDirection: SwipeSource? = null) :
+class CommunalSwipeDetector(private var lastDirection: SwipeSource.Resolved? = null) :
SwipeSourceDetector, SwipeDetector {
companion object {
private const val TRAVEL_RATIO_THRESHOLD = .5f
@@ -43,16 +43,16 @@
layoutSize: IntSize,
position: IntOffset,
density: Density,
- orientation: Orientation
- ): SwipeSource? {
+ orientation: Orientation,
+ ): SwipeSource.Resolved? {
return lastDirection
}
override fun detectSwipe(change: PointerInputChange): Boolean {
if (change.positionChange().x > 0) {
- lastDirection = Edge.Left
+ lastDirection = Edge.Resolved.Left
} else {
- lastDirection = Edge.Right
+ lastDirection = Edge.Resolved.Right
}
// Determine whether the ratio of the distance traveled horizontally to the distance
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index 8ebb42a..3644b30 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -39,7 +39,10 @@
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.TestScenes.SceneD
+import com.android.compose.test.setContentAndCreateMainScope
+import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
import org.junit.Rule
@@ -50,12 +53,7 @@
class AnimatedSharedAsStateTest {
@get:Rule val rule = createComposeRule()
- private data class Values(
- val int: Int,
- val float: Float,
- val dp: Dp,
- val color: Color,
- )
+ private data class Values(val int: Int, val float: Float, val dp: Dp, val color: Color)
private fun lerp(start: Values, stop: Values, fraction: Float): Values {
return Values(
@@ -67,10 +65,7 @@
}
@Composable
- private fun ContentScope.Foo(
- targetValues: Values,
- onCurrentValueChanged: (Values) -> Unit,
- ) {
+ private fun ContentScope.Foo(targetValues: Values, onCurrentValueChanged: (Values) -> Unit) {
val key = TestElements.Foo
Element(key, Modifier) {
val int by animateElementIntAsState(targetValues.int, key = TestValues.Value1)
@@ -242,7 +237,7 @@
fromSceneContent = {
SceneValues(
targetValues = fromValues,
- onCurrentValueChanged = { lastValueInFrom = it }
+ onCurrentValueChanged = { lastValueInFrom = it },
)
},
toSceneContent = {
@@ -406,30 +401,33 @@
}
}
- rule.setContent {
- SceneTransitionLayout(state) {
- // foo goes from 0f to 100f in A => B.
- scene(SceneA) { animateFloat(0f, foo) }
- scene(SceneB) { animateFloat(100f, foo) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ // foo goes from 0f to 100f in A => B.
+ scene(SceneA) { animateFloat(0f, foo) }
+ scene(SceneB) { animateFloat(100f, foo) }
- // bar goes from 0f to 10f in C => D.
- scene(SceneC) { animateFloat(0f, bar) }
- scene(SceneD) { animateFloat(10f, bar) }
+ // bar goes from 0f to 10f in C => D.
+ scene(SceneC) { animateFloat(0f, bar) }
+ scene(SceneD) { animateFloat(10f, bar) }
+ }
}
- }
- rule.runOnUiThread {
- // A => B is at 30%.
+ // A => B is at 30%.
+ scope.launch {
state.startTransition(
transition(
from = SceneA,
to = SceneB,
progress = { 0.3f },
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
)
+ }
- // C => D is at 70%.
+ // C => D is at 70%.
+ scope.launch {
state.startTransition(transition(from = SceneC, to = SceneD, progress = { 0.7f }))
}
rule.waitForIdle()
@@ -451,7 +449,7 @@
rule.runOnUiThread {
MutableSceneTransitionLayoutStateImpl(
SceneA,
- transitions { overscrollDisabled(SceneB, Orientation.Horizontal) }
+ transitions { overscrollDisabled(SceneB, Orientation.Horizontal) },
)
}
@@ -466,17 +464,18 @@
}
}
- rule.setContent {
- SceneTransitionLayout(state) {
- scene(SceneA) { animateFloat(0f, key) }
- scene(SceneB) { animateFloat(100f, key) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { animateFloat(0f, key) }
+ scene(SceneB) { animateFloat(100f, key) }
+ }
}
- }
// Overscroll on A at -100%: value should be interpolated given that there is no overscroll
// defined for scene A.
var progress by mutableStateOf(-1f)
- rule.runOnIdle {
+ scope.launch {
state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
}
rule.waitForIdle()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 9fa4722..fca92ca 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -41,9 +41,9 @@
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.runMonotonicClockTest
+import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,9 +53,7 @@
@RunWith(AndroidJUnit4::class)
class DraggableHandlerTest {
- private class TestGestureScope(
- val testScope: MonotonicClockTestScope,
- ) {
+ private class TestGestureScope(val testScope: MonotonicClockTestScope) {
var canChangeScene: (SceneKey) -> Boolean = { true }
val layoutState =
MutableSceneTransitionLayoutStateImpl(
@@ -83,24 +81,14 @@
}
private val scenesBuilder: SceneTransitionLayoutScope.() -> Unit = {
- scene(
- key = SceneA,
- userActions = mutableUserActionsA,
- ) {
- Text("SceneA")
- }
- scene(
- key = SceneB,
- userActions = mutableUserActionsB,
- ) {
- Text("SceneB")
- }
+ scene(key = SceneA, userActions = mutableUserActionsA) { Text("SceneA") }
+ scene(key = SceneB, userActions = mutableUserActionsB) { Text("SceneB") }
scene(
key = SceneC,
userActions =
mapOf(
Swipe.Up to SceneB,
- Swipe(SwipeDirection.Up, fromSource = Edge.Bottom) to SceneA
+ Swipe(SwipeDirection.Up, fromSource = Edge.Bottom) to SceneA,
),
) {
Text("SceneC")
@@ -110,16 +98,12 @@
userActions =
mapOf(
Swipe.Up to UserActionResult.HideOverlay(OverlayA),
- Swipe.Down to UserActionResult.ReplaceByOverlay(OverlayB)
+ Swipe.Down to UserActionResult.ReplaceByOverlay(OverlayB),
),
) {
Text("OverlayA")
}
- overlay(
- key = OverlayB,
- ) {
- Text("OverlayB")
- }
+ overlay(key = OverlayB) { Text("OverlayB") }
}
val transitionInterceptionThreshold = 0.05f
@@ -132,7 +116,10 @@
swipeSourceDetector = DefaultEdgeDetector,
transitionInterceptionThreshold = transitionInterceptionThreshold,
builder = scenesBuilder,
- coroutineScope = testScope,
+
+ // Use testScope and not backgroundScope here because backgroundScope does not
+ // work well with advanceUntilIdle(), which is used by some tests.
+ animationScope = testScope,
)
.apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) }
@@ -141,7 +128,7 @@
fun nestedScrollConnection(
nestedScrollBehavior: NestedScrollBehavior,
- isExternalOverscrollGesture: Boolean = false
+ isExternalOverscrollGesture: Boolean = false,
) =
NestedScrollHandlerImpl(
layoutImpl = layoutImpl,
@@ -151,7 +138,7 @@
isExternalOverscrollGesture = { isExternalOverscrollGesture },
pointersInfoOwner = {
PointersInfo(startedPosition = Offset.Zero, pointersDown = 1)
- }
+ },
)
.connection
@@ -197,13 +184,19 @@
fromScene: SceneKey? = null,
toScene: SceneKey? = null,
progress: Float? = null,
- isUserInputOngoing: Boolean? = null
+ previewProgress: Float? = null,
+ isInPreviewStage: Boolean? = null,
+ isUserInputOngoing: Boolean? = null,
): Transition {
val transition = assertThat(transitionState).isSceneTransition()
currentScene?.let { assertThat(transition).hasCurrentScene(it) }
fromScene?.let { assertThat(transition).hasFromScene(it) }
toScene?.let { assertThat(transition).hasToScene(it) }
progress?.let { assertThat(transition).hasProgress(it) }
+ previewProgress?.let { assertThat(transition).hasPreviewProgress(it) }
+ isInPreviewStage?.let {
+ assertThat(transition).run { if (it) isInPreviewStage() else isNotInPreviewStage() }
+ }
isUserInputOngoing?.let { assertThat(transition).hasIsUserInputOngoing(it) }
return transition
}
@@ -260,7 +253,7 @@
fun DragController.onDragStopped(
velocity: Float,
canChangeScene: Boolean = true,
- expectedConsumed: Boolean = true
+ expectedConsumed: Boolean = true,
) {
val consumed = onStop(velocity, canChangeScene)
assertThat(consumed).isEqualTo(if (expectedConsumed) velocity else 0f)
@@ -271,16 +264,13 @@
consumedByScroll: Offset = Offset.Zero,
) {
val consumedByPreScroll =
- onPreScroll(
- available = available,
- source = NestedScrollSource.Drag,
- )
+ onPreScroll(available = available, source = NestedScrollSource.Drag)
val consumed = consumedByPreScroll + consumedByScroll
onPostScroll(
consumed = consumed,
available = available - consumed,
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
}
@@ -301,8 +291,20 @@
runMonotonicClockTest {
val testGestureScope = TestGestureScope(testScope = this)
- // run the test
- testGestureScope.block()
+ try {
+ // Run the test.
+ testGestureScope.block()
+ } finally {
+ // Make sure we stop the last transition if it was not explicitly stopped, otherwise
+ // tests will time out after 10s given that the transitions are now started on the
+ // test scope. We don't use backgroundScope when starting the test transitions
+ // because coroutines started on the background scope don't work well with
+ // advanceUntilIdle(), which is used in a few tests.
+ if (testGestureScope.draggableHandler.isDrivingTransition) {
+ (testGestureScope.layoutState.transitionState as Transition)
+ .freezeAndAnimateToCurrentState()
+ }
+ }
}
}
@@ -338,6 +340,32 @@
}
@Test
+ fun onDragStoppedAfterDrag_velocityLowerThanThreshold_remainSameScene_previewAnimated() =
+ runGestureTest {
+ layoutState.transitions = transitions {
+ // set a preview for the transition
+ from(SceneA, to = SceneC, preview = {}) {}
+ }
+ val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f))
+ assertTransition(currentScene = SceneA)
+
+ dragController.onDragStopped(velocity = velocityThreshold - 0.01f)
+ runCurrent()
+
+ // verify that transition remains in preview stage and animates back to fromScene
+ assertTransition(
+ currentScene = SceneA,
+ isInPreviewStage = true,
+ previewProgress = 0.1f,
+ progress = 0f,
+ )
+
+ // wait for the stop animation
+ advanceUntilIdle()
+ assertIdle(currentScene = SceneA)
+ }
+
+ @Test
fun onDragStoppedAfterDrag_velocityAtLeastThreshold_goToNextScene() = runGestureTest {
val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f))
assertTransition(currentScene = SceneA)
@@ -368,7 +396,7 @@
currentScene = SceneA,
fromScene = SceneA,
toScene = SceneB,
- progress = 0.6f
+ progress = 0.6f,
)
// Reverse direction such that A -> C now with 0.4
@@ -377,7 +405,7 @@
currentScene = SceneA,
fromScene = SceneA,
toScene = SceneC,
- progress = 0.4f
+ progress = 0.4f,
)
// After the drag stopped scene C should be committed
@@ -416,7 +444,7 @@
currentScene = SceneC,
fromScene = SceneC,
toScene = SceneB,
- progress = -0.1f
+ progress = -0.1f,
)
// Reverse drag direction, it will consume the previous drag
@@ -425,7 +453,7 @@
currentScene = SceneC,
fromScene = SceneC,
toScene = SceneB,
- progress = 0.0f
+ progress = 0.0f,
)
// Continue reverse drag direction, it should record progress to Scene B
@@ -434,7 +462,7 @@
currentScene = SceneC,
fromScene = SceneC,
toScene = SceneB,
- progress = 0.1f
+ progress = 0.1f,
)
}
@@ -445,13 +473,13 @@
// Start dragging from the bottom
onDragStarted(
startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE),
- overSlop = up(fractionOfScreen = 0.1f)
+ overSlop = up(fractionOfScreen = 0.1f),
)
assertTransition(
currentScene = SceneC,
fromScene = SceneC,
toScene = SceneA,
- progress = 0.1f
+ progress = 0.1f,
)
}
@@ -462,14 +490,14 @@
currentScene = SceneA,
fromScene = SceneA,
toScene = SceneC,
- progress = 0.3f
+ progress = 0.3f,
)
dragController.onDragDelta(pixels = up(fractionOfScreen = 0.3f))
assertTransition(
currentScene = SceneA,
fromScene = SceneA,
toScene = SceneC,
- progress = 0.0f
+ progress = 0.0f,
)
}
@@ -490,7 +518,7 @@
currentScene = SceneA,
fromScene = SceneA,
toScene = SceneB,
- progress = 0.2f
+ progress = 0.2f,
)
// Start animation A -> B with progress 0.2 -> 1.0
@@ -505,7 +533,7 @@
currentScene = SceneB,
fromScene = SceneB,
toScene = SceneC,
- progress = 0.2f
+ progress = 0.2f,
)
// After the drag stopped scene C should be committed
@@ -599,7 +627,7 @@
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
nestedScroll.onPreScroll(
available = downOffset(fractionOfScreen = 0.1f),
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertIdle(currentScene = SceneA)
}
@@ -611,7 +639,7 @@
nestedScroll.onPostScroll(
consumed = Offset.Zero,
available = Offset.Zero,
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertIdle(currentScene = SceneA)
@@ -625,7 +653,7 @@
nestedScroll.onPostScroll(
consumed = Offset.Zero,
available = downOffset(fractionOfScreen = 0.1f),
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertTransition(currentScene = SceneA)
@@ -645,7 +673,7 @@
val consumed =
nestedScroll.onPreScroll(
available = downOffset(fractionOfScreen = 0.1f),
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertThat(progress).isEqualTo(0.2f)
@@ -653,7 +681,7 @@
nestedScroll.onPostScroll(
consumed = consumed,
available = Offset.Zero,
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertThat(progress).isEqualTo(0.2f)
@@ -664,7 +692,7 @@
private fun TestGestureScope.preScrollAfterSceneTransition(
firstScroll: Float,
- secondScroll: Float
+ secondScroll: Float,
) {
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
// start scene transition
@@ -676,7 +704,7 @@
// a pre scroll event, that could be intercepted by DraggableHandlerImpl
nestedScroll.onPreScroll(
available = Offset(0f, secondScroll),
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
}
@@ -788,7 +816,7 @@
// scroll consumed in child
nestedScroll.scroll(
available = downOffset(fractionOfScreen = 0.1f),
- consumedByScroll = downOffset(fractionOfScreen = 0.1f)
+ consumedByScroll = downOffset(fractionOfScreen = 0.1f),
)
// scroll offsetY10 is all available for parents
@@ -832,13 +860,11 @@
val nestedScroll =
nestedScrollConnection(
nestedScrollBehavior = EdgeWithPreview,
- isExternalOverscrollGesture = true
+ isExternalOverscrollGesture = true,
)
// scroll not consumed in child
- nestedScroll.scroll(
- available = downOffset(fractionOfScreen = 0.1f),
- )
+ nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f))
// scroll offsetY10 is all available for parents
nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f))
@@ -940,7 +966,7 @@
}
@Test
- fun finish() = runGestureTest {
+ fun freezeAndAnimateToCurrentState() = runGestureTest {
// Start at scene C.
navigateToSceneC()
@@ -952,35 +978,25 @@
// The current transition can be intercepted.
assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isTrue()
- // Finish the transition.
+ // Freeze the transition.
val transition = transitionState as Transition
- val job = transition.finish()
+ transition.freezeAndAnimateToCurrentState()
assertTransition(isUserInputOngoing = false)
-
- // The current transition can not be intercepted anymore.
- assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isFalse()
-
- // Calling finish() multiple times returns the same Job.
- assertThat(transition.finish()).isSameInstanceAs(job)
- assertThat(transition.finish()).isSameInstanceAs(job)
- assertThat(transition.finish()).isSameInstanceAs(job)
-
- // We can join the job to wait for the animation to end.
- assertTransition()
- job.join()
+ advanceUntilIdle()
assertIdle(SceneC)
}
@Test
- fun finish_cancelled() = runGestureTest {
- // Swipe up from the middle to transition to scene B.
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
- onDragStarted(startedPosition = middle, overSlop = up(0.1f))
- assertTransition(fromScene = SceneA, toScene = SceneB)
+ fun interruptedTransitionCanNotBeImmediatelyIntercepted() = runGestureTest {
+ assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isFalse()
+ onDragStarted(overSlop = up(0.1f))
+ assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isTrue()
- // Finish the transition and cancel the returned job.
- (transitionState as Transition).finish().cancelAndJoin()
- assertIdle(SceneA)
+ layoutState.startTransitionImmediately(
+ animationScope = testScope.backgroundScope,
+ transition(SceneA, SceneB),
+ )
+ assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isFalse()
}
@Test
@@ -1074,7 +1090,7 @@
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 1f)
// Release the finger.
- dragController.onDragStopped(velocity = -velocityThreshold)
+ dragController.onDragStopped(velocity = -velocityThreshold, expectedConsumed = false)
// Exhaust all coroutines *without advancing the clock*. Given that we are at progress >=
// 100% and that the overscroll on scene B is doing nothing, we are already idle.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 770c0f8..20a1e3c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -71,10 +71,11 @@
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.assertSizeIsEqualTo
+import com.android.compose.test.setContentAndCreateMainScope
+import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
import org.junit.Ignore
import org.junit.Rule
@@ -228,11 +229,7 @@
scene(SceneA) {
Box(Modifier.size(layoutSize)) {
// Transformed element
- Element(
- TestElements.Bar,
- elementSize,
- elementOffset,
- )
+ Element(TestElements.Bar, elementSize, elementOffset)
}
}
scene(SceneB) { Box(Modifier.size(layoutSize)) }
@@ -504,7 +501,7 @@
}
@Test
- fun elementModifierNodeIsRecycledInLazyLayouts() = runTest {
+ fun elementModifierNodeIsRecycledInLazyLayouts() {
val nPages = 2
val pagerState = PagerState(currentPage = 0) { nPages }
var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
@@ -533,10 +530,7 @@
scene(SceneA) {
// The pages are full-size and beyondBoundsPageCount is 0, so at rest only one
// page should be composed.
- HorizontalPager(
- pagerState,
- beyondViewportPageCount = 0,
- ) { page ->
+ HorizontalPager(pagerState, beyondViewportPageCount = 0) { page ->
when (page) {
0 -> Box(Modifier.element(TestElements.Foo).fillMaxSize())
1 -> Box(Modifier.fillMaxSize())
@@ -605,7 +599,7 @@
scaleSize(TestElements.Foo, width = 2f, height = 0.5f)
translate(TestElements.Foo, x = 10.dp, y = 10.dp)
fade(TestElements.Foo)
- }
+ },
) {
before { assertThat(fooCompositions).isEqualTo(1) }
at(16) { assertThat(fooCompositions).isEqualTo(1) }
@@ -626,22 +620,23 @@
from(SceneA, to = SceneB) {
scaleSize(TestElements.Foo, width = 2f, height = 2f)
}
- }
+ },
)
}
- rule.setContent {
- SceneTransitionLayout(state) {
- scene(SceneA) { Box(Modifier.element(TestElements.Foo).size(20.dp)) }
- scene(SceneB) {}
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { Box(Modifier.element(TestElements.Foo).size(20.dp)) }
+ scene(SceneB) {}
+ }
}
- }
// Pause the clock to block recompositions.
rule.mainClock.autoAdvance = false
// Change the current transition.
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.5f }))
}
@@ -674,13 +669,13 @@
touchSlop = LocalViewConfiguration.current.touchSlop
SceneTransitionLayout(
state = state,
- modifier = Modifier.size(layoutWidth, layoutHeight)
+ modifier = Modifier.size(layoutWidth, layoutHeight),
) {
scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
animateContentFloatAsState(
value = animatedFloatRange.start,
key = TestValues.Value1,
- false
+ false,
)
Spacer(Modifier.fillMaxSize())
}
@@ -689,7 +684,7 @@
animateContentFloatAsState(
value = animatedFloatRange.endInclusive,
key = TestValues.Value1,
- canOverflow = false
+ canOverflow = false,
)
Spacer(Modifier.element(TestElements.Foo).fillMaxSize())
LaunchedEffect(Unit) {
@@ -784,7 +779,7 @@
progressConverter = ProgressConverter.linear()
translate(TestElements.Foo, y = overscrollTranslateY)
}
- }
+ },
)
as MutableSceneTransitionLayoutStateImpl
}
@@ -793,7 +788,7 @@
touchSlop = LocalViewConfiguration.current.touchSlop
SceneTransitionLayout(
state = state,
- modifier = Modifier.size(layoutWidth, layoutHeight)
+ modifier = Modifier.size(layoutWidth, layoutHeight),
) {
scene(SceneA) { Spacer(Modifier.fillMaxSize()) }
scene(SceneB, userActions = mapOf(Swipe.Up to SceneA)) {
@@ -802,7 +797,7 @@
// A scrollable that does not consume the scroll gesture
.scrollable(
rememberScrollableState(consumeScrollDelta = { 0f }),
- Orientation.Vertical
+ Orientation.Vertical,
)
.fillMaxSize()
) {
@@ -867,18 +862,18 @@
touchSlop = LocalViewConfiguration.current.touchSlop
SceneTransitionLayout(
state = state,
- modifier = Modifier.size(layoutWidth, layoutHeight)
+ modifier = Modifier.size(layoutWidth, layoutHeight),
) {
scene(
SceneA,
- userActions = mapOf(Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB)
+ userActions = mapOf(Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB),
) {
Box(
Modifier
// A scrollable that does not consume the scroll gesture
.scrollable(
rememberScrollableState(consumeScrollDelta = { 0f }),
- Orientation.Vertical
+ Orientation.Vertical,
)
.fillMaxSize()
) {
@@ -1201,7 +1196,7 @@
startsOutsideLayoutBounds = false,
)
}
- }
+ },
)
}
@@ -1296,7 +1291,7 @@
}
@Test
- fun interruption() = runTest {
+ fun interruption() {
// 4 frames of animation.
val duration = 4 * 16
@@ -1336,37 +1331,41 @@
val valueInC = 200f
lateinit var layoutImpl: SceneTransitionLayoutImpl
- rule.setContent {
- SceneTransitionLayoutForTesting(
- state,
- Modifier.size(layoutSize),
- onLayoutImpl = { layoutImpl = it },
- ) {
- // In scene A, Foo is aligned at the TopStart.
- scene(SceneA) {
- Box(Modifier.fillMaxSize()) {
- Foo(sizeInA, valueInA, Modifier.align(Alignment.TopStart))
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayoutForTesting(
+ state,
+ Modifier.size(layoutSize),
+ onLayoutImpl = { layoutImpl = it },
+ ) {
+ // In scene A, Foo is aligned at the TopStart.
+ scene(SceneA) {
+ Box(Modifier.fillMaxSize()) {
+ Foo(sizeInA, valueInA, Modifier.align(Alignment.TopStart))
+ }
}
- }
- // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming
- // from B. We put it before (below) scene B so that we can check that interruptions
- // values and deltas are properly cleared once all transitions are done.
- scene(SceneC) {
- Box(Modifier.fillMaxSize()) {
- Foo(sizeInC, valueInC, Modifier.align(Alignment.BottomEnd))
+ // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when
+ // coming
+ // from B. We put it before (below) scene B so that we can check that
+ // interruptions
+ // values and deltas are properly cleared once all transitions are done.
+ scene(SceneC) {
+ Box(Modifier.fillMaxSize()) {
+ Foo(sizeInC, valueInC, Modifier.align(Alignment.BottomEnd))
+ }
}
- }
- // In scene B, Foo is aligned at the TopEnd, so it moves horizontally when coming
- // from A.
- scene(SceneB) {
- Box(Modifier.fillMaxSize()) {
- Foo(sizeInB, valueInB, Modifier.align(Alignment.TopEnd))
+ // In scene B, Foo is aligned at the TopEnd, so it moves horizontally when
+ // coming
+ // from A.
+ scene(SceneB) {
+ Box(Modifier.fillMaxSize()) {
+ Foo(sizeInB, valueInB, Modifier.align(Alignment.TopEnd))
+ }
}
}
}
- }
// The offset of Foo when idle in A, B or C.
val offsetInA = DpOffset.Zero
@@ -1390,12 +1389,12 @@
from = SceneA,
to = SceneB,
progress = { aToBProgress },
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
val offsetInAToB = lerp(offsetInA, offsetInB, aToBProgress)
val sizeInAToB = lerp(sizeInA, sizeInB, aToBProgress)
val valueInAToB = lerp(valueInA, valueInB, aToBProgress)
- rule.runOnUiThread { state.startTransition(aToB) }
+ scope.launch { state.startTransition(aToB) }
rule
.onNode(isElement(TestElements.Foo, SceneB))
.assertSizeIsEqualTo(sizeInAToB)
@@ -1415,7 +1414,7 @@
progress = { bToCProgress },
interruptionProgress = { interruptionProgress },
)
- rule.runOnUiThread { state.startTransition(bToC) }
+ scope.launch { state.startTransition(bToC) }
// The interruption deltas, which will be multiplied by the interruption progress then added
// to the current transition offset and size.
@@ -1476,10 +1475,8 @@
.assertSizeIsEqualTo(sizeInC)
// Manually finish the transition.
- rule.runOnUiThread {
- state.finishTransition(aToB)
- state.finishTransition(bToC)
- }
+ aToB.finish()
+ bToC.finish()
rule.waitForIdle()
assertThat(state.transitionState).isIdle()
@@ -1498,7 +1495,7 @@
}
@Test
- fun interruption_sharedTransitionDisabled() = runTest {
+ fun interruption_sharedTransitionDisabled() {
// 4 frames of animation.
val duration = 4 * 16
val layoutSize = DpSize(200.dp, 100.dp)
@@ -1524,21 +1521,22 @@
Box(modifier.element(TestElements.Foo).size(fooSize))
}
- rule.setContent {
- SceneTransitionLayout(state, Modifier.size(layoutSize)) {
- scene(SceneA) {
- Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopStart)) }
- }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state, Modifier.size(layoutSize)) {
+ scene(SceneA) {
+ Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopStart)) }
+ }
- scene(SceneB) {
- Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopEnd)) }
- }
+ scene(SceneB) {
+ Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopEnd)) }
+ }
- scene(SceneC) {
- Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.BottomEnd)) }
+ scene(SceneC) {
+ Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.BottomEnd)) }
+ }
}
}
- }
// The offset of Foo when idle in A, B or C.
val offsetInA = DpOffset.Zero
@@ -1547,7 +1545,12 @@
// State is a transition A => B at 50% interrupted by B => C at 30%.
val aToB =
- transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 0.5f },
+ onFreezeAndAnimate = { /* never finish */ },
+ )
var bToCInterruptionProgress by mutableStateOf(1f)
val bToC =
transition(
@@ -1555,11 +1558,11 @@
to = SceneC,
progress = { 0.3f },
interruptionProgress = { bToCInterruptionProgress },
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
- rule.runOnUiThread { state.startTransition(aToB) }
+ scope.launch { state.startTransition(aToB) }
rule.waitForIdle()
- rule.runOnUiThread { state.startTransition(bToC) }
+ scope.launch { state.startTransition(bToC) }
// Foo is placed in both B and C given that the shared transition is disabled. In B, its
// offset is impacted by the interruption but in C it is not.
@@ -1579,7 +1582,8 @@
// Manually finish A => B so only B => C is remaining.
bToCInterruptionProgress = 0f
- rule.runOnUiThread { state.finishTransition(aToB) }
+ aToB.finish()
+
rule
.onNode(isElement(TestElements.Foo, SceneB))
.assertPositionInRootIsEqualTo(offsetInB.x, offsetInB.y)
@@ -1595,7 +1599,7 @@
progress = { 0.7f },
interruptionProgress = { 1f },
)
- rule.runOnUiThread { state.startTransition(bToA) }
+ scope.launch { state.startTransition(bToA) }
// Foo should have the position it had in B right before the interruption.
rule
@@ -1609,32 +1613,35 @@
val state =
rule.runOnUiThread {
MutableSceneTransitionLayoutStateImpl(
- SceneA,
- transitions { overscrollDisabled(SceneA, Orientation.Horizontal) }
- )
- .apply {
- startTransition(
- transition(
- from = SceneA,
- to = SceneB,
- progress = { -1f },
- orientation = Orientation.Horizontal
- )
- )
- }
+ SceneA,
+ transitions { overscrollDisabled(SceneA, Orientation.Horizontal) },
+ )
}
lateinit var layoutImpl: SceneTransitionLayoutImpl
- rule.setContent {
- SceneTransitionLayoutForTesting(
- state,
- Modifier.size(100.dp),
- onLayoutImpl = { layoutImpl = it },
- ) {
- scene(SceneA) {}
- scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayoutForTesting(
+ state,
+ Modifier.size(100.dp),
+ onLayoutImpl = { layoutImpl = it },
+ ) {
+ scene(SceneA) {}
+ scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+ }
}
+
+ scope.launch {
+ state.startTransition(
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { -1f },
+ orientation = Orientation.Horizontal,
+ )
+ )
}
+ rule.waitForIdle()
assertThat(layoutImpl.elements).containsKey(TestElements.Foo)
val foo = layoutImpl.elements.getValue(TestElements.Foo)
@@ -1647,33 +1654,34 @@
}
@Test
- fun lastAlphaIsNotSetByOutdatedLayer() = runTest {
+ fun lastAlphaIsNotSetByOutdatedLayer() {
val state =
rule.runOnUiThread {
MutableSceneTransitionLayoutStateImpl(
SceneA,
- transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } }
+ transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } },
)
}
lateinit var layoutImpl: SceneTransitionLayoutImpl
- rule.setContent {
- SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
- scene(SceneA) {}
- scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
- scene(SceneC) { Box(Modifier.element(TestElements.Foo)) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+ scene(SceneA) {}
+ scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+ scene(SceneC) { Box(Modifier.element(TestElements.Foo)) }
+ }
}
- }
// Start A => B at 0.5f.
var aToBProgress by mutableStateOf(0.5f)
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(
transition(
from = SceneA,
to = SceneB,
progress = { aToBProgress },
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
)
}
@@ -1692,7 +1700,7 @@
assertThat(fooInB.lastAlpha).isEqualTo(0.7f)
// Start B => C at 0.3f.
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0.3f }))
}
rule.waitForIdle()
@@ -1715,21 +1723,22 @@
rule.runOnUiThread {
MutableSceneTransitionLayoutStateImpl(
SceneA,
- transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } }
+ transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } },
)
}
lateinit var layoutImpl: SceneTransitionLayoutImpl
- rule.setContent {
- SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
- scene(SceneA) {}
- scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+ scene(SceneA) {}
+ scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+ }
}
- }
// Start A => B at 60%.
var interruptionProgress by mutableStateOf(1f)
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(
transition(
from = SceneA,
@@ -1765,7 +1774,7 @@
transitions {
overscrollDisabled(SceneA, Orientation.Horizontal)
overscrollDisabled(SceneB, Orientation.Horizontal)
- }
+ },
)
}
@@ -1774,19 +1783,20 @@
Box(Modifier.element(TestElements.Foo).size(10.dp))
}
- rule.setContent {
- SceneTransitionLayout(state) {
- scene(SceneA) { Foo() }
- scene(SceneB) { Foo() }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { Foo() }
+ scene(SceneB) { Foo() }
+ }
}
- }
rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed()
rule.onNode(isElement(TestElements.Foo, SceneB)).assertDoesNotExist()
// A => B while overscrolling at scene B.
var progress by mutableStateOf(2f)
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
}
rule.waitForIdle()
@@ -1813,7 +1823,7 @@
transitions {
overscrollDisabled(SceneA, Orientation.Horizontal)
overscrollDisabled(SceneB, Orientation.Horizontal)
- }
+ },
)
}
@@ -1827,19 +1837,20 @@
MovableElement(key, modifier) { content { Text(text) } }
}
- rule.setContent {
- SceneTransitionLayout(state) {
- scene(SceneA) { MovableFoo(text = fooInA) }
- scene(SceneB) { MovableFoo(text = fooInB) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { MovableFoo(text = fooInA) }
+ scene(SceneB) { MovableFoo(text = fooInB) }
+ }
}
- }
rule.onNode(hasText(fooInA)).assertIsDisplayed()
rule.onNode(hasText(fooInB)).assertDoesNotExist()
// A => B while overscrolling at scene B.
var progress by mutableStateOf(2f)
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
}
rule.waitForIdle()
@@ -1858,7 +1869,7 @@
}
@Test
- fun interruptionThenOverscroll() = runTest {
+ fun interruptionThenOverscroll() {
val state =
rule.runOnUiThread {
MutableSceneTransitionLayoutStateImpl(
@@ -1868,7 +1879,7 @@
progressConverter = ProgressConverter.linear()
translate(TestElements.Foo, y = 15.dp)
}
- }
+ },
)
}
@@ -1879,22 +1890,23 @@
}
}
- rule.setContent {
- SceneTransitionLayout(state, Modifier.size(200.dp)) {
- scene(SceneA) { SceneWithFoo(offset = DpOffset.Zero) }
- scene(SceneB) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 0.dp)) }
- scene(SceneC) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 40.dp)) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state, Modifier.size(200.dp)) {
+ scene(SceneA) { SceneWithFoo(offset = DpOffset.Zero) }
+ scene(SceneB) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 0.dp)) }
+ scene(SceneC) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 40.dp)) }
+ }
}
- }
// Start A => B at 75%.
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(
transition(
from = SceneA,
to = SceneB,
progress = { 0.75f },
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
)
}
@@ -1907,7 +1919,7 @@
// Interrupt A => B with B => C at 0%.
var progress by mutableStateOf(0f)
var interruptionProgress by mutableStateOf(1f)
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(
transition(
from = SceneB,
@@ -1915,7 +1927,7 @@
progress = { progress },
interruptionProgress = { interruptionProgress },
orientation = Orientation.Vertical,
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
)
}
@@ -1963,12 +1975,13 @@
}
lateinit var layoutImpl: SceneTransitionLayoutImpl
- rule.setContent {
- SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
- scene(SceneA) { NestedFooBar() }
- scene(SceneB) { NestedFooBar() }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+ scene(SceneA) { NestedFooBar() }
+ scene(SceneB) { NestedFooBar() }
+ }
}
- }
// Idle on A: composed and placed only in B.
rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed()
@@ -1997,7 +2010,7 @@
assertThat(barInA.lastScale).isNotEqualTo(Scale.Unspecified)
// A => B: composed in both and placed only in B.
- rule.runOnUiThread { state.startTransition(transition(from = SceneA, to = SceneB)) }
+ scope.launch { state.startTransition(transition(from = SceneA, to = SceneB)) }
rule.onNode(isElement(TestElements.Foo, SceneA)).assertExists().assertIsNotDisplayed()
rule.onNode(isElement(TestElements.Bar, SceneA)).assertExists().assertIsNotDisplayed()
rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed()
@@ -2024,7 +2037,7 @@
}
@Test
- fun currentTransitionSceneIsUsedToComputeElementValues() = runTest {
+ fun currentTransitionSceneIsUsedToComputeElementValues() {
val state =
rule.runOnIdle {
MutableSceneTransitionLayoutStateImpl(
@@ -2033,7 +2046,7 @@
from(SceneB, to = SceneC) {
scaleSize(TestElements.Foo, width = 2f, height = 3f)
}
- }
+ },
)
}
@@ -2044,23 +2057,31 @@
}
}
- rule.setContent {
- SceneTransitionLayout(state, Modifier.size(200.dp)) {
- scene(SceneA) { Foo() }
- scene(SceneB) {}
- scene(SceneC) { Foo() }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state, Modifier.size(200.dp)) {
+ scene(SceneA) { Foo() }
+ scene(SceneB) {}
+ scene(SceneC) { Foo() }
+ }
}
- }
// We have 2 transitions:
// - A => B at 100%
// - B => C at 0%
// So Foo should have a size of (40dp, 60dp) in both A and C given that it is scaling its
// size in B => C.
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(
- transition(from = SceneA, to = SceneB, progress = { 1f }, onFinish = neverFinish())
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 1f },
+ onFreezeAndAnimate = { /* never finish */ },
+ )
)
+ }
+ scope.launch {
state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0f }))
}
@@ -2069,7 +2090,7 @@
}
@Test
- fun interruptionDeltasAreProperlyCleaned() = runTest {
+ fun interruptionDeltasAreProperlyCleaned() {
val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
@Composable
@@ -2079,18 +2100,24 @@
}
}
- rule.setContent {
- SceneTransitionLayout(state, Modifier.size(200.dp)) {
- scene(SceneA) { Foo(offset = 0.dp) }
- scene(SceneB) { Foo(offset = 20.dp) }
- scene(SceneC) { Foo(offset = 40.dp) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state, Modifier.size(200.dp)) {
+ scene(SceneA) { Foo(offset = 0.dp) }
+ scene(SceneB) { Foo(offset = 20.dp) }
+ scene(SceneC) { Foo(offset = 40.dp) }
+ }
}
- }
// Start A => B at 50%.
val aToB =
- transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
- rule.runOnUiThread { state.startTransition(aToB) }
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 0.5f },
+ onFreezeAndAnimate = { /* never finish */ },
+ )
+ scope.launch { state.startTransition(aToB) }
rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(10.dp, 10.dp)
// Start B => C at 0%. This will compute an interruption delta of (-10dp, -10dp) so that the
@@ -2103,9 +2130,9 @@
current = { SceneB },
progress = { 0f },
interruptionProgress = { interruptionProgress },
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
- rule.runOnUiThread { state.startTransition(bToC) }
+ scope.launch { state.startTransition(bToC) }
rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(10.dp, 10.dp)
// Finish the interruption and leave the transition progress at 0f. We should be at the same
@@ -2116,9 +2143,9 @@
// Finish both transitions but directly start a new one B => A with interruption progress
// 100%. We should be at (20dp, 20dp), unless the interruption deltas have not been
// correctly cleaned.
- rule.runOnUiThread {
- state.finishTransition(aToB)
- state.finishTransition(bToC)
+ aToB.finish()
+ bToC.finish()
+ scope.launch {
state.startTransition(
transition(
from = SceneB,
@@ -2132,12 +2159,12 @@
}
@Test
- fun lastSizeIsUnspecifiedWhenOverscrollingOtherScene() = runTest {
+ fun lastSizeIsUnspecifiedWhenOverscrollingOtherScene() {
val state =
rule.runOnIdle {
MutableSceneTransitionLayoutStateImpl(
SceneA,
- transitions { overscrollDisabled(SceneA, Orientation.Horizontal) }
+ transitions { overscrollDisabled(SceneA, Orientation.Horizontal) },
)
}
@@ -2147,17 +2174,23 @@
}
lateinit var layoutImpl: SceneTransitionLayoutImpl
- rule.setContent {
- SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
- scene(SceneA) { Foo() }
- scene(SceneB) { Foo() }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+ scene(SceneA) { Foo() }
+ scene(SceneB) { Foo() }
+ }
}
- }
// Overscroll A => B on A.
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(
- transition(from = SceneA, to = SceneB, progress = { -1f }, onFinish = neverFinish())
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { -1f },
+ onFreezeAndAnimate = { /* never finish */ },
+ )
)
}
rule.waitForIdle()
@@ -2173,7 +2206,7 @@
}
@Test
- fun transparentElementIsNotImpactingInterruption() = runTest {
+ fun transparentElementIsNotImpactingInterruption() {
val state =
rule.runOnIdle {
MutableSceneTransitionLayoutStateImpl(
@@ -2191,7 +2224,7 @@
// In B => A, Foo is shared.
sharedElement(TestElements.Foo, enabled = true)
}
- }
+ },
)
}
@@ -2200,23 +2233,24 @@
Box(modifier.element(TestElements.Foo).size(10.dp))
}
- rule.setContent {
- SceneTransitionLayout(state) {
- scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
- // Define A after B so that Foo is placed in A during A <=> B.
- scene(SceneA) { Foo() }
+ // Define A after B so that Foo is placed in A during A <=> B.
+ scene(SceneA) { Foo() }
+ }
}
- }
// Start A => B at 70%.
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(
transition(
from = SceneA,
to = SceneB,
progress = { 0.7f },
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
)
}
@@ -2227,14 +2261,14 @@
// Start B => A at 50% with interruptionProgress = 100%. Foo is placed in A and should still
// be at (40dp, 60dp) given that it was fully transparent in A before the interruption.
var interruptionProgress by mutableStateOf(1f)
- rule.runOnUiThread {
+ scope.launch {
state.startTransition(
transition(
from = SceneB,
to = SceneA,
progress = { 0.5f },
interruptionProgress = { interruptionProgress },
- onFinish = neverFinish(),
+ onFreezeAndAnimate = { /* never finish */ },
)
)
}
@@ -2250,7 +2284,7 @@
}
@Test
- fun replacedTransitionDoesNotTriggerInterruption() = runTest {
+ fun replacedTransitionDoesNotTriggerInterruption() {
val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
@Composable
@@ -2258,17 +2292,23 @@
Box(modifier.element(TestElements.Foo).size(10.dp))
}
- rule.setContent {
- SceneTransitionLayout(state) {
- scene(SceneA) { Foo() }
- scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { Foo() }
+ scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
+ }
}
- }
// Start A => B at 50%.
val aToB1 =
- transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
- rule.runOnUiThread { state.startTransition(aToB1) }
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 0.5f },
+ onFreezeAndAnimate = { /* never finish */ },
+ )
+ scope.launch { state.startTransition(aToB1) }
rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
@@ -2282,7 +2322,7 @@
interruptionProgress = { 1f },
replacedTransition = aToB1,
)
- rule.runOnUiThread { state.startTransition(aToB2) }
+ scope.launch { state.startTransition(aToB2) }
rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(40.dp, 60.dp)
}
@@ -2316,7 +2356,7 @@
},
previewProgress = 0.5f,
progress = 0f,
- isInPreviewStage = true
+ isInPreviewStage = true,
)
// verify that preview transition for exiting elements is halfway played from
@@ -2372,7 +2412,7 @@
},
previewProgress = 0.5f,
progress = 0.5f,
- isInPreviewStage = false
+ isInPreviewStage = false,
)
// verify that exiting elements remain in the preview-end state if no further transition is
@@ -2412,13 +2452,13 @@
transition: TransitionBuilder.() -> Unit,
progress: Float = 0f,
previewProgress: Float = 0.5f,
- isInPreviewStage: Boolean = true
+ isInPreviewStage: Boolean = true,
): SceneTransitionLayoutImpl {
val state =
rule.runOnIdle {
MutableSceneTransitionLayoutStateImpl(
from,
- transitions { from(from, to = to, preview = preview, builder = transition) }
+ transitions { from(from, to = to, preview = preview, builder = transition) },
)
}
@@ -2428,12 +2468,13 @@
}
lateinit var layoutImpl: SceneTransitionLayoutImpl
- rule.setContent {
- SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
- scene(from) { Box { exitingElements.forEach { Foo(it) } } }
- scene(to) { Box { enteringElements.forEach { Foo(it) } } }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+ scene(from) { Box { exitingElements.forEach { Foo(it) } } }
+ scene(to) { Box { enteringElements.forEach { Foo(it) } } }
+ }
}
- }
val bToA =
transition(
@@ -2441,9 +2482,9 @@
to = to,
progress = { progress },
previewProgress = { previewProgress },
- isInPreviewStage = { isInPreviewStage }
+ isInPreviewStage = { isInPreviewStage },
)
- rule.runOnUiThread { state.startTransition(bToA) }
+ scope.launch { state.startTransition(bToA) }
rule.waitForIdle()
return layoutImpl
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt
index cceaf57..dea9283 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt
@@ -34,7 +34,7 @@
@Test
fun horizontalEdges() {
- fun horizontalEdge(position: Int): Edge? =
+ fun horizontalEdge(position: Int): Edge.Resolved? =
detector.source(
layoutSize,
position = IntOffset(position, 0),
@@ -42,17 +42,17 @@
Orientation.Horizontal,
)
- assertThat(horizontalEdge(0)).isEqualTo(Edge.Left)
- assertThat(horizontalEdge(30)).isEqualTo(Edge.Left)
+ assertThat(horizontalEdge(0)).isEqualTo(Edge.Resolved.Left)
+ assertThat(horizontalEdge(30)).isEqualTo(Edge.Resolved.Left)
assertThat(horizontalEdge(31)).isEqualTo(null)
assertThat(horizontalEdge(69)).isEqualTo(null)
- assertThat(horizontalEdge(70)).isEqualTo(Edge.Right)
- assertThat(horizontalEdge(100)).isEqualTo(Edge.Right)
+ assertThat(horizontalEdge(70)).isEqualTo(Edge.Resolved.Right)
+ assertThat(horizontalEdge(100)).isEqualTo(Edge.Resolved.Right)
}
@Test
fun verticalEdges() {
- fun verticalEdge(position: Int): Edge? =
+ fun verticalEdge(position: Int): Edge.Resolved? =
detector.source(
layoutSize,
position = IntOffset(0, position),
@@ -60,11 +60,11 @@
Orientation.Vertical,
)
- assertThat(verticalEdge(0)).isEqualTo(Edge.Top)
- assertThat(verticalEdge(30)).isEqualTo(Edge.Top)
+ assertThat(verticalEdge(0)).isEqualTo(Edge.Resolved.Top)
+ assertThat(verticalEdge(30)).isEqualTo(Edge.Resolved.Top)
assertThat(verticalEdge(31)).isEqualTo(null)
assertThat(verticalEdge(69)).isEqualTo(null)
- assertThat(verticalEdge(70)).isEqualTo(Edge.Bottom)
- assertThat(verticalEdge(100)).isEqualTo(Edge.Bottom)
+ assertThat(verticalEdge(70)).isEqualTo(Edge.Resolved.Bottom)
+ assertThat(verticalEdge(100)).isEqualTo(Edge.Resolved.Bottom)
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index 3f6bd2c..b87cc5c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -25,9 +25,9 @@
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.runMonotonicClockTest
+import com.android.compose.test.transition
import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.launch
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,8 +44,8 @@
transitions { /* default interruption handler */ },
)
- state.setTargetScene(SceneB, coroutineScope = this)
- state.setTargetScene(SceneC, coroutineScope = this)
+ state.setTargetScene(SceneB, animationScope = this)
+ state.setTargetScene(SceneC, animationScope = this)
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
@@ -70,7 +70,7 @@
object : InterruptionHandler {
override fun onInterruption(
interrupted: TransitionState.Transition.ChangeScene,
- newTargetScene: SceneKey
+ newTargetScene: SceneKey,
): InterruptionResult {
return InterruptionResult(
animateFrom = interrupted.currentScene,
@@ -81,14 +81,14 @@
},
)
- state.setTargetScene(SceneB, coroutineScope = this)
- state.setTargetScene(SceneC, coroutineScope = this)
+ state.setTargetScene(SceneB, animationScope = this)
+ state.setTargetScene(SceneC, animationScope = this)
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
.containsExactly(
// B to C.
- Triple(SceneB, SceneC, SceneC),
+ Triple(SceneB, SceneC, SceneC)
)
.inOrder()
}
@@ -105,7 +105,7 @@
object : InterruptionHandler {
override fun onInterruption(
interrupted: TransitionState.Transition.ChangeScene,
- newTargetScene: SceneKey
+ newTargetScene: SceneKey,
): InterruptionResult {
return InterruptionResult(
animateFrom =
@@ -124,10 +124,10 @@
// Animate to B and advance the transition a little bit so that progress > visibility
// threshold and that reversing from B back to A won't immediately snap to A.
- state.setTargetScene(SceneB, coroutineScope = this)
+ state.setTargetScene(SceneB, animationScope = this)
testScheduler.advanceTimeBy(duration / 2L)
- state.setTargetScene(SceneC, coroutineScope = this)
+ state.setTargetScene(SceneC, animationScope = this)
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
@@ -155,13 +155,21 @@
// Progress must be > visibility threshold otherwise we will directly snap to A.
progress = { 0.5f },
progressVelocity = { progressVelocity },
- onFinish = { launch {} },
)
- state.startTransition(aToB)
+ state.startTransitionImmediately(animationScope = backgroundScope, aToB)
// Animate back to A. The previous transition is reversed, i.e. it has the same (from, to)
// pair, and its velocity is used when animating the progress back to 0.
- val bToA = checkNotNull(state.setTargetScene(SceneA, coroutineScope = this))
+ val bToA =
+ checkNotNull(
+ state.setTargetScene(
+ SceneA,
+ // We use testScope here and not backgroundScope because setTargetScene
+ // needs the monotonic clock that is only available in the test scope.
+ animationScope = this,
+ )
+ )
+ .first
testScheduler.runCurrent()
assertThat(bToA).hasFromScene(SceneA)
assertThat(bToA).hasToScene(SceneB)
@@ -181,13 +189,21 @@
to = SceneB,
current = { SceneA },
progressVelocity = { progressVelocity },
- onFinish = { launch {} },
)
- state.startTransition(aToB)
+ state.startTransitionImmediately(animationScope = backgroundScope, aToB)
// Animate to B. The previous transition is reversed, i.e. it has the same (from, to) pair,
// and its velocity is used when animating the progress to 1.
- val bToA = checkNotNull(state.setTargetScene(SceneB, coroutineScope = this))
+ val bToA =
+ checkNotNull(
+ state.setTargetScene(
+ SceneB,
+ // We use testScope here and not backgroundScope because setTargetScene
+ // needs the monotonic clock that is only available in the test scope.
+ animationScope = this,
+ )
+ )
+ .first
testScheduler.runCurrent()
assertThat(bToA).hasFromScene(SceneA)
assertThat(bToA).hasToScene(SceneB)
@@ -201,7 +217,7 @@
{ transition: TransitionState.Transition.ChangeScene? ->
Triple(transition?.fromScene, transition?.toScene, transition?.currentScene)
},
- "(from, to, current) triple"
+ "(from, to, current) triple",
)
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
index e1d0945..c8e7e65 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
@@ -17,6 +17,7 @@
package com.android.compose.animation.scene
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Test
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
index e4879d9..7c9e9ce 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
@@ -161,7 +161,7 @@
element: ElementKey,
transition: TransitionState.Transition,
fromContentZIndex: Float,
- toContentZIndex: Float
+ toContentZIndex: Float,
): ContentKey {
transition as TransitionState.Transition.ChangeScene
assertThat(transition).hasFromScene(SceneA)
@@ -177,7 +177,7 @@
SceneB
}
}
- }
+ },
)
rule.testTransition(
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..af717ac 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
@@ -256,7 +259,7 @@
it
}
),
- Orientation.Vertical
+ Orientation.Vertical,
)
.fillMaxSize()
)
@@ -435,6 +438,9 @@
continueDraggingDown()
assertThat(stopped).isTrue()
+
+ // Complete the gesture
+ rule.onRoot().performTouchInput { up() }
}
@Test
@@ -637,7 +643,7 @@
override fun onPostScroll(
consumed: Offset,
available: Offset,
- source: NestedScrollSource
+ source: NestedScrollSource,
): Offset {
availableOnPostScroll = available.y
return Offset.Zero
@@ -650,7 +656,7 @@
override suspend fun onPostFling(
consumed: Velocity,
- available: Velocity
+ available: Velocity,
): Velocity {
availableOnPostFling = available.y
return Velocity.Zero
@@ -719,4 +725,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/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
index d58a0a3..5edb99e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
@@ -51,7 +51,7 @@
private val layoutHeight = 400.dp
private fun setup2ScenesAndScrollTouchSlop(
- modifierSceneA: @Composable ContentScope.() -> Modifier = { Modifier },
+ modifierSceneA: @Composable ContentScope.() -> Modifier = { Modifier }
): MutableSceneTransitionLayoutState {
val state =
rule.runOnUiThread {
@@ -62,7 +62,7 @@
touchSlop = LocalViewConfiguration.current.touchSlop
SceneTransitionLayout(
state = state,
- modifier = Modifier.size(layoutWidth, layoutHeight)
+ modifier = Modifier.size(layoutWidth, layoutHeight),
) {
scene(SceneA, userActions = mapOf(Swipe.Up to SceneB)) {
Spacer(modifierSceneA().fillMaxSize())
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
index 0543e7f..596e2cd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
@@ -29,6 +29,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
@@ -46,12 +47,7 @@
@Test
fun testObservableTransitionState() = runTest {
val state =
- rule.runOnUiThread {
- MutableSceneTransitionLayoutState(
- SceneA,
- EmptyTestTransitions,
- )
- }
+ rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA, EmptyTestTransitions) }
// Collect the current observable state into [observableState].
// TODO(b/290184746): Use collectValues {} once it is extracted into a library that can be
@@ -81,7 +77,7 @@
scene(SceneA) {}
scene(SceneB) {}
}
- }
+ },
) {
before {
assertThat(observableState()).isEqualTo(ObservableTransitionState.Idle(SceneA))
@@ -89,16 +85,16 @@
at(0) {
val state = observableState()
assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
- assertThat((state as ObservableTransitionState.Transition).fromScene)
+ assertThat((state as ObservableTransitionState.Transition).fromContent)
.isEqualTo(SceneA)
- assertThat(state.toScene).isEqualTo(SceneB)
+ assertThat(state.toContent).isEqualTo(SceneB)
assertThat(state.progress()).isEqualTo(0f)
}
at(TestTransitionDuration / 2) {
val state = observableState()
- assertThat((state as ObservableTransitionState.Transition).fromScene)
+ assertThat((state as ObservableTransitionState.Transition).fromContent)
.isEqualTo(SceneA)
- assertThat(state.toScene).isEqualTo(SceneB)
+ assertThat(state.toContent).isEqualTo(SceneB)
assertThat(state.progress()).isEqualTo(0.5f)
}
after {
@@ -139,7 +135,7 @@
var transitionCurrentScene by mutableStateOf(SceneA)
val transition =
transition(from = SceneA, to = SceneB, current = { transitionCurrentScene })
- state.startTransition(transition)
+ state.startTransitionImmediately(animationScope = backgroundScope, transition)
assertThat(currentScene.value).isEqualTo(SceneA)
// Change the transition current scene.
@@ -156,7 +152,7 @@
rule.runOnUiThread {
MutableSceneTransitionLayoutState(
SceneA,
- transitions = transitions { from(SceneA, to = SceneB, preview = {}) }
+ transitions = transitions { from(SceneA, to = SceneB, preview = {}) },
)
}
rule.setContent {
@@ -199,7 +195,7 @@
var state = observableState()
assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
- assertThat((state as ObservableTransitionState.Transition).fromScene).isEqualTo(SceneA)
+ assertThat((state as ObservableTransitionState.Transition).fromContent).isEqualTo(SceneA)
assertThat(state.previewProgress()).isEqualTo(0.4f)
assertThat(state.isInPreviewStage()).isEqualTo(true)
@@ -217,7 +213,7 @@
}
state = observableState()
assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
- assertThat((state as ObservableTransitionState.Transition).fromScene).isEqualTo(SceneA)
+ assertThat((state as ObservableTransitionState.Transition).fromContent).isEqualTo(SceneA)
assertThat(state.previewProgress()).isEqualTo(0.4f)
assertThat(state.isInPreviewStage()).isEqualTo(false)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
index bec2bb2..ffed15b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
@@ -18,14 +18,17 @@
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
@@ -42,7 +45,12 @@
import com.android.compose.animation.scene.TestOverlays.OverlayB
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.test.assertSizeIsEqualTo
+import com.android.compose.test.setContentAndCreateMainScope
+import com.android.compose.test.subjects.assertThat
+import com.android.compose.test.transition
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -524,4 +532,241 @@
}
}
}
+
+ @Test
+ fun replaceAnimation_elementInCurrentSceneAndOneOverlay() {
+ val sharedIntKey = ValueKey("sharedInt")
+ val sharedIntValueByContent = mutableMapOf<ContentKey, Int>()
+
+ @Composable
+ fun SceneScope.animateContentInt(targetValue: Int) {
+ val animatedValue = animateContentIntAsState(targetValue, sharedIntKey)
+ LaunchedEffect(animatedValue) {
+ try {
+ snapshotFlow { animatedValue.value }
+ .collect { sharedIntValueByContent[contentKey] = it }
+ } finally {
+ sharedIntValueByContent.remove(contentKey)
+ }
+ }
+ }
+
+ rule.testReplaceOverlayTransition(
+ currentSceneContent = {
+ Box(Modifier.size(width = 180.dp, height = 120.dp)) {
+ animateContentInt(targetValue = 1_000)
+ Foo(width = 60.dp, height = 40.dp)
+ }
+ },
+ fromContent = {},
+ fromAlignment = Alignment.TopStart,
+ toContent = {
+ animateContentInt(targetValue = 2_000)
+ Foo(width = 100.dp, height = 80.dp)
+ },
+ transition = {
+ // 4 frames of animation
+ spec = tween(4 * 16, easing = LinearEasing)
+ },
+ ) {
+ // Foo moves from (0,0) with a size of 60x40dp to centered (in a 180x120dp Box) with a
+ // size of 100x80dp, so at (40,20).
+ //
+ // The animated Int goes from 1_000 to 2_000.
+ before {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertSizeIsEqualTo(60.dp, 40.dp)
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
+
+ assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_000)
+ assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+ assertThat(sharedIntValueByContent).doesNotContainKey(OverlayB)
+ }
+
+ at(16) {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertExists()
+ .assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule
+ .onNode(isElement(TestElements.Foo, content = OverlayB))
+ .assertSizeIsEqualTo(70.dp, 50.dp)
+ .assertPositionInRootIsEqualTo(10.dp, 5.dp)
+
+ assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_250)
+ assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+ assertThat(sharedIntValueByContent).containsEntry(OverlayB, 1_250)
+ }
+
+ at(32) {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertExists()
+ .assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule
+ .onNode(isElement(TestElements.Foo, content = OverlayB))
+ .assertSizeIsEqualTo(80.dp, 60.dp)
+ .assertPositionInRootIsEqualTo(20.dp, 10.dp)
+
+ assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_500)
+ assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+ assertThat(sharedIntValueByContent).containsEntry(OverlayB, 1_500)
+ }
+
+ at(48) {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertExists()
+ .assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule
+ .onNode(isElement(TestElements.Foo, content = OverlayB))
+ .assertSizeIsEqualTo(90.dp, 70.dp)
+ .assertPositionInRootIsEqualTo(30.dp, 15.dp)
+
+ assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_750)
+ assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+ assertThat(sharedIntValueByContent).containsEntry(OverlayB, 1_750)
+ }
+
+ after {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertExists()
+ .assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule
+ .onNode(isElement(TestElements.Foo, content = OverlayB))
+ .assertSizeIsEqualTo(100.dp, 80.dp)
+ .assertPositionInRootIsEqualTo(40.dp, 20.dp)
+
+ // Outside of transitions, the value is equal to the target value in each content.
+ assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_000)
+ assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+ assertThat(sharedIntValueByContent).containsEntry(OverlayB, 2_000)
+ }
+ }
+ }
+
+ @Test
+ fun replaceAnimation_elementInCurrentSceneAndOneOverlay_sharedElementDisabled() {
+ rule.testReplaceOverlayTransition(
+ currentSceneContent = {
+ Box(Modifier.size(width = 180.dp, height = 120.dp)) {
+ Foo(width = 60.dp, height = 40.dp)
+ }
+ },
+ fromContent = {},
+ fromAlignment = Alignment.TopStart,
+ toContent = { Foo(width = 100.dp, height = 80.dp) },
+ transition = {
+ // 4 frames of animation
+ spec = tween(4 * 16, easing = LinearEasing)
+
+ // Scale Foo to/from size 0 in each content instead of sharing it.
+ sharedElement(TestElements.Foo, enabled = false)
+ scaleSize(TestElements.Foo, width = 0f, height = 0f)
+ },
+ ) {
+ before {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertSizeIsEqualTo(60.dp, 40.dp)
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
+ }
+
+ at(16) {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertSizeIsEqualTo(45.dp, 30.dp)
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule
+ .onNode(isElement(TestElements.Foo, content = OverlayB))
+ .assertSizeIsEqualTo(25.dp, 20.dp)
+ .assertPositionInRootIsEqualTo(((180 - 25) / 2f).dp, ((120 - 20) / 2f).dp)
+ }
+
+ at(32) {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertSizeIsEqualTo(30.dp, 20.dp)
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule
+ .onNode(isElement(TestElements.Foo, content = OverlayB))
+ .assertSizeIsEqualTo(50.dp, 40.dp)
+ .assertPositionInRootIsEqualTo(((180 - 50) / 2f).dp, ((120 - 40) / 2f).dp)
+ }
+
+ at(48) {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertSizeIsEqualTo(15.dp, 10.dp)
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule
+ .onNode(isElement(TestElements.Foo, content = OverlayB))
+ .assertSizeIsEqualTo(75.dp, 60.dp)
+ .assertPositionInRootIsEqualTo(((180 - 75) / 2f).dp, ((120 - 60) / 2f).dp)
+ }
+
+ after {
+ rule
+ .onNode(isElement(TestElements.Foo, content = SceneA))
+ .assertExists()
+ .assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+ rule
+ .onNode(isElement(TestElements.Foo, content = OverlayB))
+ .assertSizeIsEqualTo(100.dp, 80.dp)
+ .assertPositionInRootIsEqualTo(40.dp, 20.dp)
+ }
+ }
+ }
+
+ @Test
+ fun overscrollingOverlay_movableElementNotInOverlay() {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ transitions {
+ // Make OverlayA overscrollable.
+ overscroll(OverlayA, orientation = Orientation.Horizontal) {
+ translate(ElementKey("elementThatDoesNotExist"), x = 10.dp)
+ }
+ },
+ )
+ }
+
+ val key = MovableElementKey("Foo", contents = setOf(SceneA))
+ val movableElementChildTag = "movableElementChildTag"
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) {
+ MovableElement(key, Modifier) {
+ content { Box(Modifier.testTag(movableElementChildTag).size(100.dp)) }
+ }
+ }
+ overlay(OverlayA) { /* Does not contain the element. */ }
+ }
+ }
+
+ // Overscroll on Overlay A.
+ scope.launch { state.startTransition(transition(SceneA, OverlayA, progress = { 1.5f })) }
+ rule
+ .onNode(hasTestTag(movableElementChildTag) and inContent(SceneA))
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+ .assertSizeIsEqualTo(100.dp)
+ .assertIsDisplayed()
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
index c5b6cdf..4224a0c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
@@ -18,6 +18,8 @@
import androidx.activity.BackEventCompat
import androidx.activity.ComponentActivity
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
@@ -65,7 +67,23 @@
@Test
fun testPredictiveBack() {
- val layoutState = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+ val transitionFrames = 2
+ val layoutState =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ SceneA,
+ transitions =
+ transitions {
+ from(SceneA, to = SceneB) {
+ spec =
+ tween(
+ durationMillis = transitionFrames * 16,
+ easing = LinearEasing,
+ )
+ }
+ },
+ )
+ }
rule.setContent {
SceneTransitionLayout(layoutState) {
scene(SceneA, mapOf(Back to SceneB)) { Box(Modifier.fillMaxSize()) }
@@ -94,12 +112,27 @@
assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
assertThat(layoutState.transitionState).isIdle()
+ rule.mainClock.autoAdvance = false
+
// Start again and commit it.
rule.runOnUiThread {
dispatcher.dispatchOnBackStarted(backEvent())
dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
dispatcher.onBackPressed()
}
+ rule.mainClock.advanceTimeByFrame()
+ rule.waitForIdle()
+ val transition2 = assertThat(layoutState.transitionState).isSceneTransition()
+ // verify that transition picks up progress from preview
+ assertThat(transition2).hasProgress(0.4f, tolerance = 0.0001f)
+
+ rule.mainClock.advanceTimeByFrame()
+ rule.waitForIdle()
+ // verify that transition is half way between preview-end-state (0.4f) and target-state (1f)
+ // after one frame
+ assertThat(transition2).hasProgress(0.7f, tolerance = 0.0001f)
+
+ rule.mainClock.autoAdvance = true
rule.waitForIdle()
assertThat(layoutState.transitionState).hasCurrentScene(SceneB)
assertThat(layoutState.transitionState).isIdle()
@@ -111,7 +144,7 @@
rule.runOnUiThread {
MutableSceneTransitionLayoutState(
SceneA,
- transitions = transitions { from(SceneA, to = SceneB, preview = {}) }
+ transitions = transitions { from(SceneA, to = SceneB, preview = {}) },
)
}
rule.setContent {
@@ -210,7 +243,7 @@
rule.runOnUiThread {
MutableSceneTransitionLayoutState(
SceneA,
- initialOverlays = setOf(OverlayA, OverlayB)
+ initialOverlays = setOf(OverlayA, OverlayB),
)
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 69f2cba..f3a3488 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -30,14 +30,18 @@
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.animation.scene.transition.link.StateLink
+import com.android.compose.animation.scene.transition.seekToScene
+import com.android.compose.test.MonotonicClockTestScope
+import com.android.compose.test.TestSceneTransition
import com.android.compose.test.runMonotonicClockTest
+import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
+import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.launch
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@@ -58,9 +62,12 @@
}
@Test
- fun isTransitioningTo_transition() {
+ fun isTransitioningTo_transition() = runTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransition(transition(from = SceneA, to = SceneB))
+ state.startTransitionImmediately(
+ animationScope = backgroundScope,
+ transition(from = SceneA, to = SceneB),
+ )
assertThat(state.isTransitioning()).isTrue()
assertThat(state.isTransitioning(from = SceneA)).isTrue()
@@ -73,17 +80,16 @@
@Test
fun setTargetScene_idleToSameScene() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
- assertThat(state.setTargetScene(SceneA, coroutineScope = this)).isNull()
+ assertThat(state.setTargetScene(SceneA, animationScope = this)).isNull()
}
@Test
fun setTargetScene_idleToDifferentScene() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
- val transition = state.setTargetScene(SceneB, coroutineScope = this)
- assertThat(transition).isNotNull()
+ val (transition, job) = checkNotNull(state.setTargetScene(SceneB, animationScope = this))
assertThat(state.transitionState).isEqualTo(transition)
- transition!!.finish().join()
+ job.join()
assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
}
@@ -91,11 +97,10 @@
fun setTargetScene_transitionToSameScene() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
- val transition = state.setTargetScene(SceneB, coroutineScope = this)
- assertThat(transition).isNotNull()
- assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNull()
+ val (_, job) = checkNotNull(state.setTargetScene(SceneB, animationScope = this))
+ assertThat(state.setTargetScene(SceneB, animationScope = this)).isNull()
- transition!!.finish().join()
+ job.join()
assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
}
@@ -103,11 +108,10 @@
fun setTargetScene_transitionToDifferentScene() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
- assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
- val transition = state.setTargetScene(SceneC, coroutineScope = this)
- assertThat(transition).isNotNull()
+ assertThat(state.setTargetScene(SceneB, animationScope = this)).isNotNull()
+ val (_, job) = checkNotNull(state.setTargetScene(SceneC, animationScope = this))
- transition!!.finish().join()
+ job.join()
assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@@ -118,7 +122,7 @@
lateinit var transition: TransitionState.Transition
val job =
launch(start = CoroutineStart.UNDISPATCHED) {
- transition = state.setTargetScene(SceneB, coroutineScope = this)!!
+ transition = checkNotNull(state.setTargetScene(SceneB, animationScope = this)).first
}
assertThat(state.transitionState).isEqualTo(transition)
@@ -127,65 +131,55 @@
assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
}
- @Test
- fun transition_finishReturnsTheSameJobWhenCalledMultipleTimes() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutState(SceneA)
- val transition = state.setTargetScene(SceneB, coroutineScope = this)
- assertThat(transition).isNotNull()
-
- val job = transition!!.finish()
- assertThat(transition.finish()).isSameInstanceAs(job)
- assertThat(transition.finish()).isSameInstanceAs(job)
- assertThat(transition.finish()).isSameInstanceAs(job)
- }
-
private fun setupLinkedStates(
parentInitialScene: SceneKey = SceneC,
childInitialScene: SceneKey = SceneA,
sourceFrom: SceneKey? = SceneA,
sourceTo: SceneKey? = SceneB,
targetFrom: SceneKey? = SceneC,
- targetTo: SceneKey = SceneD
+ targetTo: SceneKey = SceneD,
): Pair<MutableSceneTransitionLayoutStateImpl, MutableSceneTransitionLayoutStateImpl> {
val parentState = MutableSceneTransitionLayoutState(parentInitialScene)
val link =
listOf(
StateLink(
parentState,
- listOf(StateLink.TransitionLink(sourceFrom, sourceTo, targetFrom, targetTo))
+ listOf(StateLink.TransitionLink(sourceFrom, sourceTo, targetFrom, targetTo)),
)
)
val childState = MutableSceneTransitionLayoutState(childInitialScene, stateLinks = link)
return Pair(
parentState as MutableSceneTransitionLayoutStateImpl,
- childState as MutableSceneTransitionLayoutStateImpl
+ childState as MutableSceneTransitionLayoutStateImpl,
)
}
@Test
- fun linkedTransition_startsLinkAndFinishesLinkInToState() {
+ fun linkedTransition_startsLinkAndFinishesLinkInToState() = runTest {
val (parentState, childState) = setupLinkedStates()
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition)
+ val job =
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
- childState.finishTransition(childTransition)
+ childTransition.finish()
+ job.join()
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
}
@Test
- fun linkedTransition_transitiveLink() {
+ fun linkedTransition_transitiveLink() = runTest {
val parentParentState =
MutableSceneTransitionLayoutState(SceneB) as MutableSceneTransitionLayoutStateImpl
val parentLink =
listOf(
StateLink(
parentParentState,
- listOf(StateLink.TransitionLink(SceneC, SceneD, SceneB, SceneC))
+ listOf(StateLink.TransitionLink(SceneC, SceneD, SceneB, SceneC)),
)
)
val parentState =
@@ -195,7 +189,7 @@
listOf(
StateLink(
parentState,
- listOf(StateLink.TransitionLink(SceneA, SceneB, SceneC, SceneD))
+ listOf(StateLink.TransitionLink(SceneA, SceneB, SceneC, SceneD)),
)
)
val childState =
@@ -204,25 +198,27 @@
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition)
+ val job =
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue()
- childState.finishTransition(childTransition)
+ childTransition.finish()
+ job.join()
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
assertThat(parentParentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@Test
- fun linkedTransition_linkProgressIsEqual() {
+ fun linkedTransition_linkProgressIsEqual() = runTest {
val (parentState, childState) = setupLinkedStates()
var progress = 0f
val childTransition = transition(SceneA, SceneB, progress = { progress })
- childState.startTransition(childTransition)
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
assertThat(parentState.currentTransition?.progress).isEqualTo(0f)
progress = .5f
@@ -230,28 +226,32 @@
}
@Test
- fun linkedTransition_reverseTransitionIsNotLinked() {
+ fun linkedTransition_reverseTransitionIsNotLinked() = runTest {
val (parentState, childState) = setupLinkedStates()
val childTransition = transition(SceneB, SceneA, current = { SceneB })
- childState.startTransition(childTransition)
+ val job =
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue()
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
- childState.finishTransition(childTransition)
+ childTransition.finish()
+ job.join()
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@Test
- fun linkedTransition_startsLinkAndFinishesLinkInFromState() {
+ fun linkedTransition_startsLinkAndFinishesLinkInFromState() = runTest {
val (parentState, childState) = setupLinkedStates()
val childTransition = transition(SceneA, SceneB, current = { SceneA })
- childState.startTransition(childTransition)
+ val job =
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- childState.finishTransition(childTransition)
+ childTransition.finish()
+ job.join()
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@@ -260,22 +260,14 @@
fun linkedTransition_startsLinkButLinkedStateIsTakenOver() = runTest {
val (parentState, childState) = setupLinkedStates()
- val childTransition =
- transition(
- SceneA,
- SceneB,
- onFinish = { launch { /* Do nothing. */ } },
- )
- val parentTransition =
- transition(
- SceneC,
- SceneA,
- onFinish = { launch { /* Do nothing. */ } },
- )
- childState.startTransition(childTransition)
- parentState.startTransition(parentTransition)
+ val childTransition = transition(SceneA, SceneB)
+ val parentTransition = transition(SceneC, SceneA)
+ val job =
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
+ parentState.startTransitionImmediately(animationScope = backgroundScope, parentTransition)
- childState.finishTransition(childTransition)
+ childTransition.finish()
+ job.join()
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(parentTransition)
}
@@ -297,22 +289,18 @@
)
// Default transition from A to B.
- assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
+ assertThat(state.setTargetScene(SceneB, animationScope = this)).isNotNull()
assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(1)
// Go back to A.
- state.setTargetScene(SceneA, coroutineScope = this)
+ state.setTargetScene(SceneA, animationScope = this)
testScheduler.advanceUntilIdle()
assertThat(state.transitionState).isIdle()
assertThat(state.transitionState).hasCurrentScene(SceneA)
// Specific transition from A to B.
assertThat(
- state.setTargetScene(
- SceneB,
- coroutineScope = this,
- transitionKey = transitionkey,
- )
+ state.setTargetScene(SceneB, animationScope = this, transitionKey = transitionkey)
)
.isNotNull()
assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(2)
@@ -321,8 +309,9 @@
@Test
fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransition(
- transition(from = SceneA, to = SceneB, current = { SceneA }, progress = { 0.2f })
+ state.startTransitionImmediately(
+ animationScope = backgroundScope,
+ transition(from = SceneA, to = SceneB, current = { SceneA }, progress = { 0.2f }),
)
assertThat(state.isTransitioning()).isTrue()
@@ -339,7 +328,10 @@
@Test
fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.8f }))
+ state.startTransitionImmediately(
+ animationScope = backgroundScope,
+ transition(from = SceneA, to = SceneB, progress = { 0.8f }),
+ )
assertThat(state.isTransitioning()).isTrue()
// Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -356,18 +348,12 @@
fun snapToIdleIfClose_multipleTransitions() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- val aToB =
- transition(
- from = SceneA,
- to = SceneB,
- progress = { 0.5f },
- onFinish = { launch { /* do nothing */ } },
- )
- state.startTransition(aToB)
+ val aToB = transition(from = SceneA, to = SceneB, progress = { 0.5f })
+ state.startTransitionImmediately(animationScope = backgroundScope, aToB)
assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f })
- state.startTransition(bToC)
+ state.startTransitionImmediately(animationScope = backgroundScope, bToC)
assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
// Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -385,13 +371,14 @@
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
var progress by mutableStateOf(0f)
var currentScene by mutableStateOf(SceneB)
- state.startTransition(
+ state.startTransitionImmediately(
+ animationScope = backgroundScope,
transition(
from = SceneA,
to = SceneB,
current = { currentScene },
- progress = { progress }
- )
+ progress = { progress },
+ ),
)
assertThat(state.isTransitioning()).isTrue()
@@ -406,62 +393,63 @@
}
@Test
- fun linkedTransition_fuzzyLinksAreMatchedAndStarted() {
+ fun linkedTransition_fuzzyLinksAreMatchedAndStarted() = runTest {
val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD)
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition)
+ val job =
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
- childState.finishTransition(childTransition)
+ childTransition.finish()
+ job.join()
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
}
@Test
- fun linkedTransition_fuzzyLinksAreMatchedAndResetToProperPreviousScene() {
+ fun linkedTransition_fuzzyLinksAreMatchedAndResetToProperPreviousScene() = runTest {
val (parentState, childState) =
setupLinkedStates(SceneC, SceneA, SceneA, null, null, SceneD)
val childTransition = transition(SceneA, SceneB, current = { SceneA })
- childState.startTransition(childTransition)
+ val job =
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
- childState.finishTransition(childTransition)
+ childTransition.finish()
+ job.join()
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@Test
- fun linkedTransition_fuzzyLinksAreNotMatched() {
+ fun linkedTransition_fuzzyLinksAreNotMatched() = runTest {
val (parentState, childState) =
setupLinkedStates(SceneC, SceneA, SceneB, null, SceneC, SceneD)
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition)
+ childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse()
}
- private fun startOverscrollableTransistionFromAtoB(
+ private fun MonotonicClockTestScope.startOverscrollableTransistionFromAtoB(
progress: () -> Float,
sceneTransitions: SceneTransitions,
): MutableSceneTransitionLayoutStateImpl {
- val state =
- MutableSceneTransitionLayoutStateImpl(
- SceneA,
- sceneTransitions,
- )
- state.startTransition(
+ val state = MutableSceneTransitionLayoutStateImpl(SceneA, sceneTransitions)
+ state.startTransitionImmediately(
+ animationScope = backgroundScope,
transition(
from = SceneA,
to = SceneB,
progress = progress,
orientation = Orientation.Vertical,
- )
+ ),
)
assertThat(state.isTransitioning()).isTrue()
return state
@@ -476,7 +464,7 @@
sceneTransitions =
transitions {
overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
- }
+ },
)
val transition = assertThat(state.transitionState).isSceneTransition()
assertThat(transition).hasNoOverscrollSpec()
@@ -507,7 +495,7 @@
sceneTransitions =
transitions {
overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
- }
+ },
)
val transition = assertThat(state.transitionState).isSceneTransition()
@@ -536,7 +524,7 @@
val state =
startOverscrollableTransistionFromAtoB(
progress = { progress.value },
- sceneTransitions = transitions {}
+ sceneTransitions = transitions {},
)
val transition = assertThat(state.transitionState).isSceneTransition()
@@ -560,54 +548,54 @@
@Test
fun multipleTransitions() = runTest {
- val finishingTransitions = mutableSetOf<TransitionState.Transition>()
- fun onFinish(transition: TransitionState.Transition): Job {
- // Instead of letting the transition finish, we put the transition in the
- // finishingTransitions set so that we can verify that finish() is called when expected
- // and then we call state STLState.finishTransition() ourselves.
- finishingTransitions.add(transition)
+ val frozenTransitions = mutableSetOf<TestSceneTransition>()
+ fun onFreezeAndAnimate(transition: TestSceneTransition): () -> Unit {
+ // Instead of letting the transition finish when it is frozen, we put the transition in
+ // the frozenTransitions set so that we can verify that freezeAndAnimateToCurrentState()
+ // is called when expected and then we call finish() ourselves to finish the
+ // transitions.
+ frozenTransitions.add(transition)
- return backgroundScope.launch {
- // Try to acquire a locked mutex so that this code never completes.
- Mutex(locked = true).withLock {}
- }
+ return { /* do nothing */ }
}
val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
- val aToB = transition(SceneA, SceneB, onFinish = ::onFinish)
- val bToC = transition(SceneB, SceneC, onFinish = ::onFinish)
- val cToA = transition(SceneC, SceneA, onFinish = ::onFinish)
+ val aToB = transition(SceneA, SceneB, onFreezeAndAnimate = ::onFreezeAndAnimate)
+ val bToC = transition(SceneB, SceneC, onFreezeAndAnimate = ::onFreezeAndAnimate)
+ val cToA = transition(SceneC, SceneA, onFreezeAndAnimate = ::onFreezeAndAnimate)
// Starting state.
- assertThat(finishingTransitions).isEmpty()
+ assertThat(frozenTransitions).isEmpty()
assertThat(state.currentTransitions).isEmpty()
// A => B.
- state.startTransition(aToB)
- assertThat(finishingTransitions).isEmpty()
+ val aToBJob = state.startTransitionImmediately(animationScope = backgroundScope, aToB)
+ assertThat(frozenTransitions).isEmpty()
assertThat(state.finishedTransitions).isEmpty()
assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
- // B => C. This should automatically call finish() on aToB.
- state.startTransition(bToC)
- assertThat(finishingTransitions).containsExactly(aToB)
+ // B => C. This should automatically call freezeAndAnimateToCurrentState() on aToB.
+ val bToCJob = state.startTransitionImmediately(animationScope = backgroundScope, bToC)
+ assertThat(frozenTransitions).containsExactly(aToB)
assertThat(state.finishedTransitions).isEmpty()
assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
- // C => A. This should automatically call finish() on bToC.
- state.startTransition(cToA)
- assertThat(finishingTransitions).containsExactly(aToB, bToC)
+ // C => A. This should automatically call freezeAndAnimateToCurrentState() on bToC.
+ state.startTransitionImmediately(animationScope = backgroundScope, cToA)
+ assertThat(frozenTransitions).containsExactly(aToB, bToC)
assertThat(state.finishedTransitions).isEmpty()
assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
// Mark bToC as finished. The list of current transitions does not change because aToB is
// still not marked as finished.
- state.finishTransition(bToC)
+ bToC.finish()
+ bToCJob.join()
assertThat(state.finishedTransitions).containsExactly(bToC)
assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
// Mark aToB as finished. This will remove both aToB and bToC from the list of transitions.
- state.finishTransition(aToB)
+ aToB.finish()
+ aToBJob.join()
assertThat(state.finishedTransitions).isEmpty()
assertThat(state.currentTransitions).containsExactly(cToA).inOrder()
}
@@ -617,8 +605,9 @@
val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
fun startTransition() {
- val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */ } })
- state.startTransition(transition)
+ val transition =
+ transition(SceneA, SceneB, onFreezeAndAnimate = { launch { /* do nothing */ } })
+ state.startTransitionImmediately(animationScope = backgroundScope, transition)
}
var hasLoggedWtf = false
@@ -641,7 +630,7 @@
val state = MutableSceneTransitionLayoutState(SceneA)
// Transition to B.
- state.setTargetScene(SceneB, coroutineScope = this)
+ state.setTargetScene(SceneB, animationScope = this)
val transition = assertThat(state.transitionState).isSceneTransition()
assertThat(transition).hasCurrentScene(SceneB)
@@ -650,4 +639,92 @@
assertThat(state.transitionState).isIdle()
assertThat(state.transitionState).hasCurrentScene(SceneC)
}
+
+ @Test
+ fun snapToScene_freezesCurrentTransition() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutStateImpl(SceneA)
+
+ // Start a transition that is never finished. We don't use backgroundScope on purpose so
+ // that this test would fail if the transition was not frozen when snapping.
+ state.startTransitionImmediately(animationScope = this, transition(SceneA, SceneB))
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneB)
+
+ // Snap to C.
+ state.snapToScene(SceneC)
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneC)
+ }
+
+ @Test
+ fun seekToScene() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ val progress = Channel<Float>()
+
+ val job =
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ state.seekToScene(SceneB, progress.consumeAsFlow())
+ }
+
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneB)
+ assertThat(transition).hasProgress(0f)
+
+ // Change progress.
+ progress.send(0.4f)
+ assertThat(transition).hasProgress(0.4f)
+
+ // Close the channel normally to confirm the transition.
+ progress.close()
+ job.join()
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneB)
+ }
+
+ @Test
+ fun seekToScene_cancelled() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ val progress = Channel<Float>()
+
+ val job =
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ state.seekToScene(SceneB, progress.consumeAsFlow())
+ }
+
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneB)
+ assertThat(transition).hasProgress(0f)
+
+ // Change progress.
+ progress.send(0.4f)
+ assertThat(transition).hasProgress(0.4f)
+
+ // Close the channel with a CancellationException to cancel the transition.
+ progress.close(CancellationException())
+ job.join()
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneA)
+ }
+
+ @Test
+ fun seekToScene_interrupted() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ val progress = Channel<Float>()
+
+ val job =
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ state.seekToScene(SceneB, progress.consumeAsFlow())
+ }
+
+ assertThat(state.transitionState).isSceneTransition()
+
+ // Start a new transition, interrupting the seek transition.
+ state.setTargetScene(SceneB, animationScope = this)
+
+ // The previous job is cancelled and does not infinitely collect the progress.
+ job.join()
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index b8e13da..400f0b3 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -55,10 +55,13 @@
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.assertSizeIsEqualTo
+import com.android.compose.test.setContentAndCreateMainScope
import com.android.compose.test.subjects.DpOffsetSubject
import com.android.compose.test.subjects.assertThat
+import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
@@ -92,14 +95,8 @@
)
}
- SceneTransitionLayout(
- state = layoutState,
- modifier = Modifier.size(LayoutSize),
- ) {
- scene(
- SceneA,
- userActions = mapOf(Back to SceneB),
- ) {
+ SceneTransitionLayout(state = layoutState, modifier = Modifier.size(LayoutSize)) {
+ scene(SceneA, userActions = mapOf(Back to SceneB)) {
Box(Modifier.fillMaxSize()) {
SharedFoo(size = 50.dp, childOffset = 0.dp, Modifier.align(Alignment.TopEnd))
Text("SceneA")
@@ -247,7 +244,7 @@
sharedFoo.assertHeightIsEqualTo(75.dp)
sharedFoo.assertPositionInRootIsEqualTo(
expectedTop = 0.dp,
- expectedLeft = (LayoutSize - 50.dp) / 2
+ expectedLeft = (LayoutSize - 50.dp) / 2,
)
// The shared offset of the single child of SharedFoo() is 50dp in scene B and 0dp in Scene
@@ -322,22 +319,23 @@
rule.runOnUiThread {
MutableSceneTransitionLayoutStateImpl(
SceneA,
- transitions { overscrollDisabled(SceneB, Orientation.Horizontal) }
+ transitions { overscrollDisabled(SceneB, Orientation.Horizontal) },
)
}
val layoutTag = "layout"
- rule.setContent {
- SceneTransitionLayout(state, Modifier.testTag(layoutTag)) {
- scene(SceneA) { Box(Modifier.size(50.dp)) }
- scene(SceneB) { Box(Modifier.size(70.dp)) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state, Modifier.testTag(layoutTag)) {
+ scene(SceneA) { Box(Modifier.size(50.dp)) }
+ scene(SceneB) { Box(Modifier.size(70.dp)) }
+ }
}
- }
// Overscroll on A at -100%: size should be interpolated given that there is no overscroll
// defined for scene A.
var progress by mutableStateOf(-1f)
- rule.runOnIdle {
+ scope.launch {
state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
}
rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(30.dp)
@@ -367,7 +365,7 @@
from(SceneB, to = SceneC) {
spec = tween(duration.toInt(), easing = LinearEasing)
}
- }
+ },
)
}
@@ -443,7 +441,7 @@
}
private fun SemanticsNodeInteraction.offsetRelativeTo(
- other: SemanticsNodeInteraction,
+ other: SemanticsNodeInteraction
): DpOffset {
val node = fetchSemanticsNode()
val bounds = node.boundsInRoot
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index e48cd817..25e8713 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -385,12 +385,9 @@
SceneTransitionLayout(
state = layoutState,
- modifier = Modifier.size(LayoutWidth, LayoutHeight)
+ modifier = Modifier.size(LayoutWidth, LayoutHeight),
) {
- scene(
- SceneA,
- userActions = mapOf(Swipe.Down to SceneB),
- ) {
+ scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
Spacer(Modifier.fillMaxSize())
}
scene(SceneB) { Spacer(Modifier.fillMaxSize()) }
@@ -507,7 +504,7 @@
fade(TestElements.Foo)
fade(TestElements.Bar)
}
- }
+ },
)
var touchSlop = 0f
@@ -519,8 +516,8 @@
userActions =
mapOf(
Swipe.Down to SceneB,
- Swipe.Up to UserActionResult(SceneB, transitionKey = transitionkey)
- )
+ Swipe.Up to UserActionResult(SceneB, transitionKey = transitionkey),
+ ),
) {
Box(Modifier.fillMaxSize())
}
@@ -565,7 +562,7 @@
val state =
layoutState(
SceneA,
- transitions { from(SceneA, to = SceneB) { distance = swipeDistance } }
+ transitions { from(SceneA, to = SceneB) { distance = swipeDistance } },
)
val layoutSize = 200.dp
@@ -617,7 +614,7 @@
progressConverter = ProgressConverter.linear()
translate(TestElements.Foo, x = { 20.dp.toPx() }, y = { 30.dp.toPx() })
}
- }
+ },
)
}
val layoutSize = 200.dp
@@ -801,7 +798,7 @@
override fun onPostScroll(
consumed: Offset,
available: Offset,
- source: NestedScrollSource
+ source: NestedScrollSource,
): Offset {
availableOnPostScroll = available.y
return super.onPostScroll(consumed, available, source)
@@ -814,7 +811,7 @@
transitions {
from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) }
overscrollDisabled(SceneB, Orientation.Vertical)
- }
+ },
)
}
val layoutSize = 200.dp
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index bed6cef..223af80 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -62,7 +62,7 @@
.comparingElementsUsing(
Correspondence.transforming<TransitionSpecImpl, Pair<ContentKey?, ContentKey?>>(
{ it?.from to it?.to },
- "has (from, to) equal to"
+ "has (from, to) equal to",
)
)
.containsExactly(
@@ -111,7 +111,7 @@
fractionRange(
start = 0.1f,
end = 0.8f,
- easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f),
) {
fade(TestElements.Foo)
}
@@ -126,11 +126,7 @@
TransformationRange(start = 0.1f, end = 0.8f),
TransformationRange(start = 0.2f, end = TransformationRange.BoundUnspecified),
TransformationRange(start = TransformationRange.BoundUnspecified, end = 0.9f),
- TransformationRange(
- start = 0.1f,
- end = 0.8f,
- CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
- ),
+ TransformationRange(start = 0.1f, end = 0.8f, CubicBezierEasing(0.1f, 0.1f, 0f, 1f)),
)
}
@@ -146,7 +142,7 @@
timestampRange(
startMillis = 100,
endMillis = 300,
- easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f),
) {
fade(TestElements.Foo)
}
@@ -164,7 +160,7 @@
TransformationRange(
start = 100 / 500f,
end = 300 / 500f,
- easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f),
),
)
}
@@ -200,7 +196,7 @@
preview = { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } },
reversePreview = {
fractionRange(start = 0.5f, end = 0.6f) { fade(TestElements.Foo) }
- }
+ },
) {
spec = tween(500)
fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
@@ -226,9 +222,46 @@
assertThat(previewTransformations)
.comparingElementsUsing(TRANSFORMATION_RANGE)
- .containsExactly(
- TransformationRange(start = 0.5f, end = 0.6f),
+ .containsExactly(TransformationRange(start = 0.5f, end = 0.6f))
+ }
+
+ @Test
+ fun defaultPredictiveBack() {
+ val transitions = transitions {
+ from(
+ TestScenes.SceneA,
+ to = TestScenes.SceneB,
+ preview = { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } },
+ ) {
+ spec = tween(500)
+ fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
+ timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
+ }
+ }
+
+ // Verify that fetching the transitionSpec with the PredictiveBack key defaults to the above
+ // transition despite it not having the PredictiveBack key set.
+ val transitionSpec =
+ transitions.transitionSpec(
+ from = TestScenes.SceneA,
+ to = TestScenes.SceneB,
+ key = TransitionKey.PredictiveBack,
)
+
+ val transformations = transitionSpec.transformationSpec().transformations
+
+ assertThat(transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 0.1f, end = 0.8f),
+ TransformationRange(start = 100 / 500f, end = 300 / 500f),
+ )
+
+ val previewTransformations = transitionSpec.previewTransformationSpec()?.transformations
+
+ assertThat(previewTransformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(TransformationRange(start = 0.1f, end = 0.8f))
}
@Test
@@ -298,7 +331,7 @@
private val TRANSFORMATION_RANGE =
Correspondence.transforming<Transformation, TransformationRange?>(
{ it?.range },
- "has range equal to"
+ "has range equal to",
)
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
index 44e0ba5..313379f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
@@ -39,7 +39,7 @@
/** Assert on a [TransitionState.Transition.ShowOrHideOverlay]. */
fun assertThat(
- transition: TransitionState.Transition.ShowOrHideOverlay,
+ transition: TransitionState.Transition.ShowOrHideOverlay
): ShowOrHideOverlayTransitionSubject {
return Truth.assertAbout(ShowOrHideOverlayTransitionSubject.showOrHideOverlayTransitions())
.that(transition)
@@ -47,17 +47,15 @@
/** Assert on a [TransitionState.Transition.ReplaceOverlay]. */
fun assertThat(
- transition: TransitionState.Transition.ReplaceOverlay,
+ transition: TransitionState.Transition.ReplaceOverlay
): ReplaceOverlayTransitionSubject {
return Truth.assertAbout(ReplaceOverlayTransitionSubject.replaceOverlayTransitions())
.that(transition)
}
class TransitionStateSubject
-private constructor(
- metadata: FailureMetadata,
- private val actual: TransitionState,
-) : Subject(metadata, actual) {
+private constructor(metadata: FailureMetadata, private val actual: TransitionState) :
+ Subject(metadata, actual) {
fun hasCurrentScene(sceneKey: SceneKey) {
check("currentScene").that(actual.currentScene).isEqualTo(sceneKey)
}
@@ -181,10 +179,8 @@
}
class SceneTransitionSubject
-private constructor(
- metadata: FailureMetadata,
- actual: TransitionState.Transition.ChangeScene,
-) : BaseTransitionSubject<TransitionState.Transition.ChangeScene>(metadata, actual) {
+private constructor(metadata: FailureMetadata, actual: TransitionState.Transition.ChangeScene) :
+ BaseTransitionSubject<TransitionState.Transition.ChangeScene>(metadata, actual) {
fun hasFromScene(sceneKey: SceneKey) {
check("fromScene").that(actual.fromScene).isEqualTo(sceneKey)
}
@@ -223,10 +219,8 @@
}
class ReplaceOverlayTransitionSubject
-private constructor(
- metadata: FailureMetadata,
- actual: TransitionState.Transition.ReplaceOverlay,
-) : BaseTransitionSubject<TransitionState.Transition.ReplaceOverlay>(metadata, actual) {
+private constructor(metadata: FailureMetadata, actual: TransitionState.Transition.ReplaceOverlay) :
+ BaseTransitionSubject<TransitionState.Transition.ReplaceOverlay>(metadata, actual) {
fun hasFromOverlay(fromOverlay: OverlayKey) {
check("fromOverlay").that(actual.fromOverlay).isEqualTo(fromOverlay)
}
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/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
index c9f71da..de55e2f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
@@ -57,7 +57,7 @@
transition = {
spec = tween(16 * 4, easing = LinearEasing)
anchoredSize(TestElements.Bar, TestElements.Foo)
- }
+ },
)
}
@@ -73,7 +73,7 @@
// Scale during 4 frames.
spec = tween(16 * 4, easing = LinearEasing)
anchoredSize(TestElements.Bar, TestElements.Foo)
- }
+ },
)
}
@@ -103,7 +103,7 @@
transition = {
spec = tween(16 * 4, easing = LinearEasing)
anchoredSize(TestElements.Bar, TestElements.Foo, anchorWidth = false)
- }
+ },
)
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
index 46075c3..5bfc947 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
@@ -27,7 +27,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestElements
import com.android.compose.animation.scene.testTransition
-import com.android.compose.animation.scene.transition
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
index 00acb13..4877cd6 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
@@ -56,7 +56,7 @@
transition = {
spec = tween(16 * 4, easing = LinearEasing)
// Elements should be shared by default.
- }
+ },
) {
before {
onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
index ce4c5275..a406e13 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
@@ -117,7 +117,7 @@
scrollConnection.onPostScroll(
consumed = Offset.Zero,
available = Offset(x = 0f, y = -1f),
- source = scrollSource
+ source = scrollSource,
)
// It should ignore all onPostScroll events
@@ -147,7 +147,7 @@
scrollConnection.onPostScroll(
consumed = Offset.Zero,
available = Offset(x = 0f, y = 1f),
- source = scrollSource
+ source = scrollSource,
)
// It can increase by 1 the height
@@ -162,13 +162,13 @@
scrollConnection.onPostScroll(
consumed = Offset.Zero,
available = Offset(x = 0f, y = 0.5f),
- source = scrollSource
+ source = scrollSource,
)
val offsetConsumed =
scrollConnection.onPreScroll(
available = Offset(x = 0f, y = 0.5f),
- source = scrollSource
+ source = scrollSource,
)
assertThat(offsetConsumed).isEqualTo(Offset(0f, 0.5f))
@@ -185,7 +185,7 @@
scrollConnection.onPostScroll(
consumed = Offset.Zero,
available = Offset(x = 0f, y = 1f),
- source = scrollSource
+ source = scrollSource,
)
// It should not change the height (already at max)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 8a9a92e..7f1af05 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -70,7 +70,7 @@
scrollConnection.onPostScroll(
consumed = Offset.Zero,
available = Offset.Zero,
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertThat(isStarted).isEqualTo(false)
@@ -89,7 +89,7 @@
scrollConnection.onPostScroll(
consumed = Offset.Zero,
available = Offset.Zero,
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
}
@@ -115,7 +115,7 @@
scrollConnection.onPostScroll(
consumed = Offset.Zero,
available = Offset.Zero,
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertThat(isStarted).isEqualTo(false)
@@ -130,7 +130,7 @@
scrollConnection.onPostScroll(
consumed = offset1,
available = offset2,
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertThat(lastScroll).isEqualTo(offset2)
@@ -184,10 +184,7 @@
fun receive_onPostFling() = runTest {
canStartPostFling = true
- scrollConnection.onPostFling(
- consumed = velocity1,
- available = velocity2,
- )
+ scrollConnection.onPostFling(consumed = velocity1, available = velocity2)
assertThat(lastStop).isEqualTo(velocity2)
}
@@ -202,7 +199,7 @@
scrollConnection.onPostScroll(
consumed = Offset.Zero,
available = Offset.Zero,
- source = NestedScrollSource.Drag
+ source = NestedScrollSource.Drag,
)
assertThat(isStarted).isEqualTo(false)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/SetContentAndCreateScope.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/SetContentAndCreateScope.kt
new file mode 100644
index 0000000..0819dd9
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/SetContentAndCreateScope.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.test
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+
+/**
+ * Set [content] as this rule's content and return a [CoroutineScope] bound to [Dispatchers.Main]
+ * and scoped to this rule.
+ */
+fun ComposeContentTestRule.setContentAndCreateMainScope(
+ content: @Composable () -> Unit
+): CoroutineScope {
+ lateinit var coroutineScope: CoroutineScope
+ setContent {
+ coroutineScope = rememberCoroutineScope(getContext = { Dispatchers.Main })
+ content()
+ }
+ return coroutineScope
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
new file mode 100644
index 0000000..646cff8
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.test
+
+import androidx.compose.foundation.gestures.Orientation
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState.Transition
+import kotlinx.coroutines.CompletableDeferred
+
+/** A [Transition.ShowOrHideOverlay] for tests that will be finished once [finish] is called. */
+abstract class TestOverlayTransition(
+ fromScene: SceneKey,
+ overlay: OverlayKey,
+ replacedTransition: Transition?,
+) :
+ Transition.ShowOrHideOverlay(
+ overlay = overlay,
+ fromOrToScene = fromScene,
+ fromContent = fromScene,
+ toContent = overlay,
+ replacedTransition = replacedTransition,
+ ) {
+ private val finishCompletable = CompletableDeferred<Unit>()
+
+ override suspend fun run() {
+ finishCompletable.await()
+ }
+
+ /** Finish this transition. */
+ fun finish() {
+ finishCompletable.complete(Unit)
+ }
+}
+
+/** A utility to easily create a [TestOverlayTransition] in tests. */
+fun transition(
+ fromScene: SceneKey,
+ overlay: OverlayKey,
+ isEffectivelyShown: () -> Boolean = { true },
+ progress: () -> Float = { 0f },
+ progressVelocity: () -> Float = { 0f },
+ previewProgress: () -> Float = { 0f },
+ previewProgressVelocity: () -> Float = { 0f },
+ isInPreviewStage: () -> Boolean = { false },
+ interruptionProgress: () -> Float = { 0f },
+ isInitiatedByUserInput: Boolean = false,
+ isUserInputOngoing: Boolean = false,
+ isUpOrLeft: Boolean = false,
+ bouncingContent: ContentKey? = null,
+ orientation: Orientation = Orientation.Horizontal,
+ onFreezeAndAnimate: ((TestOverlayTransition) -> Unit)? = null,
+ replacedTransition: Transition? = null,
+): TestOverlayTransition {
+ return object :
+ TestOverlayTransition(fromScene, overlay, replacedTransition),
+ TransitionState.HasOverscrollProperties {
+ override val isEffectivelyShown: Boolean
+ get() = isEffectivelyShown()
+
+ override val progress: Float
+ get() = progress()
+
+ override val progressVelocity: Float
+ get() = progressVelocity()
+
+ override val previewProgress: Float
+ get() = previewProgress()
+
+ override val previewProgressVelocity: Float
+ get() = previewProgressVelocity()
+
+ override val isInPreviewStage: Boolean
+ get() = isInPreviewStage()
+
+ override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
+ override val isUserInputOngoing: Boolean = isUserInputOngoing
+ override val isUpOrLeft: Boolean = isUpOrLeft
+ override val bouncingContent: ContentKey? = bouncingContent
+ override val orientation: Orientation = orientation
+ override val absoluteDistance = 0f
+
+ override fun freezeAndAnimateToCurrentState() {
+ if (onFreezeAndAnimate != null) {
+ onFreezeAndAnimate(this)
+ } else {
+ finish()
+ }
+ }
+
+ override fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float {
+ return interruptionProgress()
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
similarity index 63%
rename from packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
index 467031a..d24b895 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
@@ -14,17 +14,35 @@
* limitations under the License.
*/
-package com.android.compose.animation.scene
+package com.android.compose.test
import androidx.compose.foundation.gestures.Orientation
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-import kotlinx.coroutines.test.TestScope
+import com.android.compose.animation.scene.content.state.TransitionState.Transition
+import kotlinx.coroutines.CompletableDeferred
-/** A utility to easily create a [TransitionState.Transition] in tests. */
+/** A [Transition.ChangeScene] for tests that will be finished once [finish] is called. */
+abstract class TestSceneTransition(
+ fromScene: SceneKey,
+ toScene: SceneKey,
+ replacedTransition: Transition?,
+) : Transition.ChangeScene(fromScene, toScene, replacedTransition) {
+ private val finishCompletable = CompletableDeferred<Unit>()
+
+ override suspend fun run() {
+ finishCompletable.await()
+ }
+
+ /** Finish this transition. */
+ fun finish() {
+ finishCompletable.complete(Unit)
+ }
+}
+
+/** A utility to easily create a [TestSceneTransition] in tests. */
fun transition(
from: SceneKey,
to: SceneKey,
@@ -40,12 +58,11 @@
isUpOrLeft: Boolean = false,
bouncingContent: ContentKey? = null,
orientation: Orientation = Orientation.Horizontal,
- onFinish: ((TransitionState.Transition) -> Job)? = null,
- replacedTransition: TransitionState.Transition? = null,
-): TransitionState.Transition.ChangeScene {
+ onFreezeAndAnimate: ((TestSceneTransition) -> Unit)? = null,
+ replacedTransition: Transition? = null,
+): TestSceneTransition {
return object :
- TransitionState.Transition.ChangeScene(from, to, replacedTransition),
- TransitionState.HasOverscrollProperties {
+ TestSceneTransition(from, to, replacedTransition), TransitionState.HasOverscrollProperties {
override val currentScene: SceneKey
get() = current()
@@ -71,14 +88,12 @@
override val orientation: Orientation = orientation
override val absoluteDistance = 0f
- override fun finish(): Job {
- val onFinish =
- onFinish
- ?: error(
- "onFinish() must be provided if finish() is called on test transitions"
- )
-
- return onFinish(this)
+ override fun freezeAndAnimateToCurrentState() {
+ if (onFreezeAndAnimate != null) {
+ onFreezeAndAnimate(this)
+ } else {
+ finish()
+ }
}
override fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float {
@@ -86,16 +101,3 @@
}
}
}
-
-/**
- * Return a onFinish lambda that can be used with [transition] so that the transition never
- * finishes. This allows to keep the transition in the current transitions list.
- */
-fun TestScope.neverFinish(): (TransitionState.Transition) -> Job {
- return {
- backgroundScope.launch {
- // Try to acquire a locked mutex so that this code never completes.
- Mutex(locked = true).withLock {}
- }
- }
-}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
index bf7bf98..ab31038 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
@@ -30,10 +30,8 @@
}
/** A Truth subject to assert on [DpOffset] with some tolerance. Inspired by FloatSubject. */
-class DpOffsetSubject(
- metadata: FailureMetadata,
- private val actual: DpOffset,
-) : Subject(metadata, actual) {
+class DpOffsetSubject(metadata: FailureMetadata, private val actual: DpOffset) :
+ Subject(metadata, actual) {
fun isWithin(tolerance: Dp): TolerantDpOffsetComparison {
return object : TolerantDpOffsetComparison {
override fun of(expected: DpOffset) {
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
index c5a5173..0d2fcfc 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
@@ -101,15 +101,12 @@
runOnUiThread {
MutableSceneTransitionLayoutState(
fromScene,
- transitions { from(fromScene, to = toScene, builder = transition) }
+ transitions { from(fromScene, to = toScene, builder = transition) },
)
},
to = toScene,
transitionLayout = { state ->
- SceneTransitionLayout(
- state,
- layoutModifier,
- ) {
+ SceneTransitionLayout(state, layoutModifier) {
scene(fromScene, content = fromSceneContent)
scene(toScene, content = toSceneContent)
}
@@ -212,14 +209,14 @@
data class TransitionRecordingSpec(
val recordBefore: Boolean = true,
val recordAfter: Boolean = true,
- val timeSeriesCapture: TimeSeriesCaptureScope<SemanticsNodeInteractionsProvider>.() -> Unit
+ val timeSeriesCapture: TimeSeriesCaptureScope<SemanticsNodeInteractionsProvider>.() -> Unit,
)
/** Captures the feature using [capture] on the [element]. */
fun TimeSeriesCaptureScope<SemanticsNodeInteractionsProvider>.featureOfElement(
element: ElementKey,
capture: FeatureCapture<SemanticsNode, *>,
- name: String = "${element.debugName}_${capture.name}"
+ name: String = "${element.debugName}_${capture.name}",
) {
feature(isElement(element), capture, name)
}
@@ -238,7 +235,7 @@
toolkit.composeContentTestRule.runOnUiThread {
MutableSceneTransitionLayoutState(
fromScene,
- transitions { from(fromScene, to = toScene, builder = transition) }
+ transitions { from(fromScene, to = toScene, builder = transition) },
)
}
@@ -246,14 +243,11 @@
content = { play ->
LaunchedEffect(play) {
if (play) {
- state.setTargetScene(toScene, coroutineScope = this)
+ state.setTargetScene(toScene, animationScope = this)
}
}
- SceneTransitionLayout(
- state,
- layoutModifier,
- ) {
+ SceneTransitionLayout(state, layoutModifier) {
scene(fromScene, content = fromSceneContent)
scene(toScene, content = toSceneContent)
}
@@ -264,8 +258,8 @@
},
recordBefore = recordingSpec.recordBefore,
recordAfter = recordingSpec.recordAfter,
- timeSeriesCapture = recordingSpec.timeSeriesCapture
- )
+ timeSeriesCapture = recordingSpec.timeSeriesCapture,
+ ),
)
}
@@ -284,7 +278,7 @@
testTransition(
state = state,
- changeState = { state -> state.setTargetScene(to, coroutineScope = this) },
+ changeState = { state -> state.setTargetScene(to, animationScope = this) },
transitionLayout = transitionLayout,
builder = builder,
)
@@ -302,7 +296,7 @@
object : TransitionTestAssertionScope {
override fun onElement(
element: ElementKey,
- scene: SceneKey?
+ scene: SceneKey?,
): SemanticsNodeInteraction {
return onNode(isElement(element, scene))
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
index 362e23d..96d79df 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
@@ -71,7 +71,7 @@
.stateIn(
scope = backgroundScope,
started = SharingStarted.Eagerly,
- initialValue = false,
+ initialValue = true,
)
/** The default duration for DND mode when enabled. See [Settings.Secure.ZEN_DURATION]. */
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
index 2f50bbd..0ac15c5 100644
--- a/packages/SystemUI/docs/scene.md
+++ b/packages/SystemUI/docs/scene.md
@@ -121,11 +121,11 @@
do so by defining their own scene. This section describes how to do that.
Each scene is defined as an implementation of the
-[`ComposableScene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt)
+[`Scene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt)
interface, which has three parts: 1. The `key` property returns the
[`SceneKey`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt)
-that uniquely identifies that scene 2. The `destinationScenes` `Flow` returns
-the (potentially ever-changing) set of navigation edges to other scenes, based
+that uniquely identifies that scene 2. The `userActions` `Flow` returns
+the (potentially ever-changing) set of navigation edges to other content, based
on user-actions, which is how the navigation graph is defined (see
[the Scene navigation](#Scene-navigation) section for more) 3. The `Content`
function which uses
@@ -138,10 +138,10 @@
For example:
```kotlin
-@SysUISingleton class YourScene @Inject constructor( /* your dependencies here */ ) : ComposableScene {
+@SysUISingleton class YourScene @Inject constructor( /* your dependencies here */ ) : Scene {
override val key = SceneKey.YourScene
- override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
+ override val userActions: StateFlow<Map<UserAction, SceneModel>> =
MutableStateFlow<Map<UserAction, SceneModel>>(
mapOf(
// This is where scene navigation is defined, more on that below.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index fe9105e..062d351 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -84,6 +84,7 @@
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@Mock private lateinit var postureController: DevicePostureController
+ @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
@Captor private lateinit var keyListenerArgumentCaptor: ArgumentCaptor<View.OnKeyListener>
private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
@@ -131,6 +132,8 @@
fakeFeatureFlags,
mSelectedUserInteractor,
keyguardKeyboardInteractor,
+ null,
+ mUserActivityNotifier
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index e2bdc49..bb15208 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -30,10 +30,12 @@
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
@@ -89,6 +91,9 @@
@Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
+ private val kosmos = testKosmos()
+ private val msdlPlayer = kosmos.msdlPlayer
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -112,7 +117,8 @@
mKeyguardMessageAreaControllerFactory,
mPostureController,
fakeFeatureFlags,
- mSelectedUserInteractor
+ mSelectedUserInteractor,
+ msdlPlayer,
)
mKeyguardPatternView.onAttachedToWindow()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 0054d13..1076d90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -87,6 +87,8 @@
private View mOkButton;
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private UserActivityNotifier mUserActivityNotifier;
private NumPadKey[] mButtons = new NumPadKey[]{};
private KeyguardPinBasedInputViewController mKeyguardPinViewController;
@@ -117,7 +119,7 @@
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
mEmergencyButtonController, mFalsingCollector, featureFlags,
- mSelectedUserInteractor, keyguardKeyboardInteractor) {
+ mSelectedUserInteractor, keyguardKeyboardInteractor, null, mUserActivityNotifier) {
@Override
public void onResume(int reason) {
super.onResume(reason);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index ca585ab..e444db4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -18,10 +18,8 @@
package com.android.keyguard
import android.app.admin.DevicePolicyManager
-import android.app.admin.flags.Flags as DevicePolicyFlags
import android.content.res.Configuration
import android.media.AudioManager
-import android.platform.test.annotations.EnableFlags
import android.telephony.TelephonyManager
import android.testing.TestableLooper.RunWithLooper
import android.testing.TestableResources
@@ -154,6 +152,7 @@
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var postureController: DevicePostureController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
@Captor
private lateinit var swipeListenerArgumentCaptor:
@@ -239,6 +238,8 @@
featureFlags,
mSelectedUserInteractor,
keyguardKeyboardInteractor,
+ null,
+ mUserActivityNotifier
)
kosmos = testKosmos()
@@ -939,7 +940,6 @@
}
@Test
- @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
fun showAlmostAtWipeDialog_calledOnMainUser_setsCorrectUserType() {
val mainUserId = 10
@@ -956,7 +956,6 @@
}
@Test
- @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
fun showAlmostAtWipeDialog_calledOnNonMainUser_setsCorrectUserType() {
val secondaryUserId = 10
val mainUserId = 0
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
index 8f9b7c8..12c866f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
@@ -30,11 +30,11 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -48,7 +48,7 @@
@RunWith(AndroidJUnit4.class)
public class MirrorWindowControlTest extends SysuiTestCase {
- @Mock WindowManager mWindowManager;
+ @Mock ViewCaptureAwareWindowManager mWindowManager;
View mView;
int mViewWidth;
int mViewHeight;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
index 460461a..176c3ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
@@ -22,10 +22,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
@@ -38,13 +39,15 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val secureSettings = kosmos.fakeSettings
+
@Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
// mocks
@Mock private lateinit var a11yManager: AccessibilityManager
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
- private val secureSettings = FakeSettings()
private val userA11yQsShortcutsRepositoryFactory =
object : UserA11yQsShortcutsRepository.Factory {
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 66%
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..fc57757 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,16 +14,21 @@
* 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.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.user.utils.FakeUserScopedService
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -38,9 +43,10 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@Suppress("UnspecifiedRegisterReceiverFlag")
@RunWith(AndroidJUnit4::class)
-class CaptioningRepositoryTest {
+class CaptioningRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
@Captor
private lateinit var listenerCaptor: ArgumentCaptor<CaptioningManager.CaptioningChangeListener>
@@ -49,34 +55,33 @@
private lateinit var underTest: CaptioningRepository
- private val testScope = TestScope()
-
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
underTest =
- CaptioningRepositoryImpl(
- captioningManager,
- testScope.testScheduler,
- testScope.backgroundScope
- )
+ with(kosmos) {
+ CaptioningRepositoryImpl(
+ FakeUserScopedService(captioningManager),
+ userRepository,
+ testScope.testScheduler,
+ applicationCoroutineScope,
+ )
+ }
}
@Test
fun isSystemAudioCaptioningEnabled_change_repositoryEmits() {
- testScope.runTest {
- `when`(captioningManager.isEnabled).thenReturn(false)
- val isSystemAudioCaptioningEnabled = mutableListOf<Boolean>()
- underTest.isSystemAudioCaptioningEnabled
- .onEach { isSystemAudioCaptioningEnabled.add(it) }
- .launchIn(backgroundScope)
+ kosmos.testScope.runTest {
+ `when`(captioningManager.isSystemAudioCaptioningEnabled).thenReturn(false)
+ val models by collectValues(underTest.captioningModel.filterNotNull())
runCurrent()
+ `when`(captioningManager.isSystemAudioCaptioningEnabled).thenReturn(true)
triggerOnSystemAudioCaptioningChange()
runCurrent()
- assertThat(isSystemAudioCaptioningEnabled)
+ assertThat(models.map { it.isSystemAudioCaptioningEnabled })
.containsExactlyElementsIn(listOf(false, true))
.inOrder()
}
@@ -84,18 +89,16 @@
@Test
fun isSystemAudioCaptioningUiEnabled_change_repositoryEmits() {
- testScope.runTest {
- `when`(captioningManager.isSystemAudioCaptioningUiEnabled).thenReturn(false)
- val isSystemAudioCaptioningUiEnabled = mutableListOf<Boolean>()
- underTest.isSystemAudioCaptioningUiEnabled
- .onEach { isSystemAudioCaptioningUiEnabled.add(it) }
- .launchIn(backgroundScope)
+ kosmos.testScope.runTest {
+ `when`(captioningManager.isEnabled).thenReturn(false)
+ val models by collectValues(underTest.captioningModel.filterNotNull())
runCurrent()
+ `when`(captioningManager.isSystemAudioCaptioningUiEnabled).thenReturn(true)
triggerSystemAudioCaptioningUiChange()
runCurrent()
- assertThat(isSystemAudioCaptioningUiEnabled)
+ assertThat(models.map { it.isSystemAudioCaptioningUiEnabled })
.containsExactlyElementsIn(listOf(false, true))
.inOrder()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
index 4e1f82c..801d359 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
@@ -23,11 +23,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -41,9 +42,10 @@
private val testUser1 = UserHandle.of(1)!!
private val testUser2 = UserHandle.of(2)!!
- private val testDispatcher = StandardTestDispatcher()
- private val scope = TestScope(testDispatcher)
- private val settings: FakeSettings = FakeSettings()
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
private lateinit var underTest: ColorCorrectionRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
index b99dec4..2f457be 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
@@ -23,11 +23,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -41,9 +42,10 @@
private val testUser1 = UserHandle.of(1)!!
private val testUser2 = UserHandle.of(2)!!
- private val testDispatcher = StandardTestDispatcher()
- private val scope = TestScope(testDispatcher)
- private val settings: FakeSettings = FakeSettings()
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
private lateinit var underTest: ColorInversionRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
index 5757f67..54dbed8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
@@ -26,7 +26,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
@@ -38,8 +40,6 @@
import com.google.common.truth.Truth.assertThat
import java.time.LocalTime
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -51,7 +51,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testUser = UserHandle.of(1)!!
private val testStartTime = LocalTime.MIDNIGHT
private val testEndTime = LocalTime.NOON
@@ -71,8 +71,8 @@
}
private val globalSettings = kosmos.fakeGlobalSettings
private val secureSettings = kosmos.fakeSettings
- private val testDispatcher = StandardTestDispatcher()
- private val scope = TestScope(testDispatcher)
+ private val testDispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
private val userScopedColorDisplayManager =
mock<UserScopedService<ColorDisplayManager>> {
whenever(forUser(eq(testUser))).thenReturn(colorDisplayManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
index 1378dac..729d356 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
@@ -22,11 +22,12 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -39,9 +40,10 @@
private val testUser1 = UserHandle.of(1)!!
private val testUser2 = UserHandle.of(2)!!
- private val testDispatcher = StandardTestDispatcher()
- private val scope = TestScope(testDispatcher)
- private val settings: FakeSettings = FakeSettings()
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
private val underTest: OneHandedModeRepository =
OneHandedModeRepositoryImpl(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
index ce22e28..62f13f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
@@ -21,10 +21,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,9 +33,10 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class UserA11yQsShortcutsRepositoryTest : SysuiTestCase() {
- private val secureSettings = FakeSettings()
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val secureSettings = kosmos.fakeSettings
private val underTest =
UserA11yQsShortcutsRepository(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt
index 1386092..b7b98d4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt
@@ -18,6 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Provider
import org.junit.Before
@@ -41,10 +42,12 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var dialogProvider: Provider<ExtraDimDialogDelegate>
+ @Mock private lateinit var dialogTransitionAnimator: DialogTransitionAnimator
@Before
fun setUp() {
- extraDimDialogManager = ExtraDimDialogManager(dialogProvider, activityStarter)
+ extraDimDialogManager =
+ ExtraDimDialogManager(dialogProvider, activityStarter, dialogTransitionAnimator)
}
@Test
@@ -56,7 +59,7 @@
/* cancelAction= */ eq(null),
/* dismissShade= */ eq(false),
/* afterKeyguardGone= */ eq(true),
- /* deferred= */ eq(false)
+ /* deferred= */ eq(false),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index d7acaaf..80de087 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -33,7 +33,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import org.junit.Before;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 4373c88..46f076a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -46,7 +46,7 @@
import com.android.systemui.accessibility.MotionEventHelper;
import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
index 09aa2868..ca23228 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
@@ -17,11 +17,11 @@
package com.android.systemui.accessibility.hearingaid;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.bluetooth.BluetoothDevice;
import android.testing.TestableLooper;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -51,6 +51,8 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ private static final int TEST_LAUNCH_SOURCE_ID = 1;
+
private final FakeExecutor mMainExecutor = new FakeExecutor(new FakeSystemClock());
private final FakeExecutor mBackgroundExecutor = new FakeExecutor(new FakeSystemClock());
@Mock
@@ -70,7 +72,7 @@
@Before
public void setUp() {
- when(mDialogFactory.create(anyBoolean())).thenReturn(mDialogDelegate);
+ when(mDialogFactory.create(anyBoolean(), anyInt())).thenReturn(mDialogDelegate);
when(mDialogDelegate.createDialog()).thenReturn(mDialog);
mManager = new HearingDevicesDialogManager(
@@ -86,21 +88,22 @@
public void showDialog_existHearingDevice_showPairNewDeviceFalse() {
when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(true);
- mManager.showDialog(mExpandable);
+ mManager.showDialog(mExpandable, TEST_LAUNCH_SOURCE_ID);
mBackgroundExecutor.runAllReady();
mMainExecutor.runAllReady();
- verify(mDialogFactory).create(eq(/* showPairNewDevice= */ false));
+ verify(mDialogFactory).create(eq(/* showPairNewDevice= */ false),
+ eq(TEST_LAUNCH_SOURCE_ID));
}
@Test
public void showDialog_noHearingDevice_showPairNewDeviceTrue() {
when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(false);
- mManager.showDialog(mExpandable);
+ mManager.showDialog(mExpandable, TEST_LAUNCH_SOURCE_ID);
mBackgroundExecutor.runAllReady();
mMainExecutor.runAllReady();
- verify(mDialogFactory).create(eq(/* showPairNewDevice= */ true));
+ verify(mDialogFactory).create(eq(/* showPairNewDevice= */ true), eq(TEST_LAUNCH_SOURCE_ID));
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 1cd9d76..72163e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -31,9 +31,7 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.whenever
@@ -58,14 +56,13 @@
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>
- @Mock private lateinit var tableLogger: TableLogBuffer
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val clock = FakeSystemClock()
private val userRepository = FakeUserRepository()
- private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
+ private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
private lateinit var underTest: AuthenticationRepository
@@ -78,8 +75,6 @@
userRepository.setUserInfos(USER_INFOS)
runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
whenever(getSecurityMode.apply(anyInt())).thenAnswer { currentSecurityMode }
- mobileConnectionsRepository =
- FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
underTest =
AuthenticationRepositoryImpl(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 0c5e726..080b48a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -17,8 +17,6 @@
package com.android.systemui.authentication.domain.interactor
import android.app.admin.DevicePolicyManager
-import android.app.admin.flags.Flags as DevicePolicyFlags
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
@@ -414,7 +412,6 @@
}
@Test
- @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
fun upcomingWipe() =
testScope.runTest {
val upcomingWipe by collectLastValue(underTest.upcomingWipe)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index b7d99d2..65825b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -97,6 +97,8 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import dagger.Lazy;
import org.junit.Before;
@@ -185,6 +187,8 @@
private Resources mResources;
@Mock
private VibratorHelper mVibratorHelper;
+ @Mock
+ private MSDLPlayer mMSDLPlayer;
private TestableContext mContextSpy;
private Execution mExecution;
@@ -1066,7 +1070,7 @@
() -> mLogContextInteractor, () -> mPromptSelectionInteractor,
() -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper,
- mLazyViewCapture);
+ mLazyViewCapture, mMSDLPlayer);
}
@Override
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 75a77cf..4bc71fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -27,6 +27,17 @@
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.systemui.SysuiTestableContext
+import com.android.systemui.biometrics.data.repository.biometricStatusRepository
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
/** Create [FingerprintSensorPropertiesInternal] for a test. */
internal fun fingerprintSensorPropertiesInternal(
@@ -145,3 +156,67 @@
info.negativeButtonText = negativeButton
return info
}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+internal fun TestScope.updateSfpsIndicatorRequests(
+ kosmos: Kosmos,
+ mContext: SysuiTestableContext,
+ primaryBouncerRequest: Boolean? = null,
+ alternateBouncerRequest: Boolean? = null,
+ biometricPromptRequest: Boolean? = null,
+ // TODO(b/365182034): update when rest to unlock feature is implemented
+ // progressBarShowing: Boolean? = null
+) {
+ biometricPromptRequest?.let { hasBiometricPromptRequest ->
+ if (hasBiometricPromptRequest) {
+ kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.BiometricPromptAuthentication
+ )
+ } else {
+ kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ }
+ }
+
+ primaryBouncerRequest?.let { hasPrimaryBouncerRequest ->
+ updatePrimaryBouncer(
+ kosmos,
+ mContext,
+ isShowing = hasPrimaryBouncerRequest,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
+ }
+
+ alternateBouncerRequest?.let { hasAlternateBouncerRequest ->
+ kosmos.keyguardBouncerRepository.setAlternateVisible(hasAlternateBouncerRequest)
+ }
+
+ // TODO(b/365182034): set progress bar visibility when rest to unlock feature is implemented
+
+ runCurrent()
+}
+
+internal fun updatePrimaryBouncer(
+ kosmos: Kosmos,
+ mContext: SysuiTestableContext,
+ isShowing: Boolean,
+ isAnimatingAway: Boolean,
+ fpsDetectionRunning: Boolean,
+ isUnlockingWithFpAllowed: Boolean,
+) {
+ kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
+ kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
+ val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+ kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
+ primaryStartDisappearAnimation
+ )
+
+ whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
+ .thenReturn(fpsDetectionRunning)
+ whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+ .thenReturn(isUnlockingWithFpAllowed)
+ mContext.orCreateTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, true)
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt
new file mode 100644
index 0000000..298b54a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt
@@ -0,0 +1,174 @@
+/*
+ * 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.biometrics.domain.interactor
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.biometricStatusRepository
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.biometrics.updateSfpsIndicatorRequests
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayStateRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected](setAsMainLooper = true)
+class SideFpsOverlayInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val underTest = kosmos.sideFpsOverlayInteractor
+
+ @Test
+ fun verifyIsShowingFalse_whenInRearDisplayMode() {
+ kosmos.testScope.runTest {
+ val isShowing by collectLastValue(underTest.isShowing)
+ setupTestConfiguration(isInRearDisplayMode = true)
+
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+ runCurrent()
+
+ assertThat(isShowing).isFalse()
+ }
+ }
+
+ @Test
+ fun verifyIsShowingUpdates_onPrimaryBouncerShowAndHide() {
+ kosmos.testScope.runTest {
+ val isShowing by collectLastValue(underTest.isShowing)
+ setupTestConfiguration(isInRearDisplayMode = false)
+
+ // Show primary bouncer
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+ runCurrent()
+
+ assertThat(isShowing).isTrue()
+
+ // Hide primary bouncer
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false)
+ runCurrent()
+
+ assertThat(isShowing).isFalse()
+ }
+ }
+
+ @Test
+ fun verifyIsShowingUpdates_onAlternateBouncerShowAndHide() {
+ kosmos.testScope.runTest {
+ val isShowing by collectLastValue(underTest.isShowing)
+ setupTestConfiguration(isInRearDisplayMode = false)
+
+ updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true)
+ runCurrent()
+
+ assertThat(isShowing).isTrue()
+
+ // Hide alternate bouncer
+ updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false)
+ runCurrent()
+
+ assertThat(isShowing).isFalse()
+ }
+ }
+
+ @Test
+ fun verifyIsShowingUpdates_onSystemServerAuthenticationStartedAndStopped() {
+ kosmos.testScope.runTest {
+ val isShowing by collectLastValue(underTest.isShowing)
+ setupTestConfiguration(isInRearDisplayMode = false)
+
+ updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
+ runCurrent()
+
+ assertThat(isShowing).isTrue()
+
+ // System server authentication stopped
+ updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false)
+ runCurrent()
+
+ assertThat(isShowing).isFalse()
+ }
+ }
+
+ // On progress bar shown - hide indicator
+ // On progress bar hidden - show indicator
+ // TODO(b/365182034): update + enable when rest to unlock feature is implemented
+ @Ignore("b/365182034")
+ @Test
+ fun verifyIsShowingUpdates_onProgressBarInteraction() {
+ kosmos.testScope.runTest {
+ val isShowing by collectLastValue(underTest.isShowing)
+ setupTestConfiguration(isInRearDisplayMode = false)
+
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+ runCurrent()
+
+ assertThat(isShowing).isTrue()
+
+ // updateSfpsIndicatorRequests(
+ // kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
+ // true
+ // )
+ runCurrent()
+
+ assertThat(isShowing).isFalse()
+
+ // Set progress bar invisible
+ // updateSfpsIndicatorRequests(
+ // kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
+ // false
+ // )
+ runCurrent()
+
+ // Verify indicator shown
+ assertThat(isShowing).isTrue()
+ }
+ }
+
+ private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) {
+ kosmos.fingerprintPropertyRepository.setProperties(
+ sensorId = 1,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.POWER_BUTTON,
+ sensorLocations = emptyMap()
+ )
+
+ kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
+ kosmos.displayRepository.emitDisplayChangeEvent(0)
+ runCurrent()
+
+ kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ // TODO(b/365182034): set progress bar visibility once rest to unlock feature is implemented
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 7fa165c..2eea668 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -16,64 +16,48 @@
package com.android.systemui.biometrics.ui.binder
-import android.animation.Animator
-import android.graphics.Rect
-import android.hardware.biometrics.SensorLocationInternal
-import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManagerGlobal
import android.testing.TestableLooper
-import android.view.Display
-import android.view.DisplayInfo
import android.view.LayoutInflater
import android.view.View
-import android.view.ViewPropertyAnimator
-import android.view.WindowInsets
import android.view.WindowManager
-import android.view.WindowMetrics
import android.view.layoutInflater
import android.view.windowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
-import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
-import com.android.systemui.biometrics.data.repository.biometricStatusRepository
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
-import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
+import com.android.systemui.biometrics.updateSfpsIndicatorRequests
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.displayStateRepository
-import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.any
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
-import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.firstValue
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -83,84 +67,25 @@
private val kosmos = testKosmos()
@JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
- @Mock private lateinit var displayManager: DisplayManager
- @Mock
- private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
@Mock private lateinit var layoutInflater: LayoutInflater
@Mock private lateinit var sideFpsView: View
-
- private val contextDisplayInfo = DisplayInfo()
-
- private var displayWidth: Int = 0
- private var displayHeight: Int = 0
- private var boundsWidth: Int = 0
- private var boundsHeight: Int = 0
-
- private lateinit var deviceConfig: DeviceConfig
- private lateinit var sensorLocation: SensorLocationInternal
-
- enum class DeviceConfig {
- X_ALIGNED,
- Y_ALIGNED,
- }
+ @Captor private lateinit var viewCaptor: ArgumentCaptor<View>
@Before
fun setup() {
allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
-
- mContext = spy(mContext)
-
- val resources = mContext.resources
- whenever(mContext.display)
- .thenReturn(
- Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
- )
-
kosmos.layoutInflater = layoutInflater
-
- whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
- .thenReturn(MutableStateFlow(false))
-
- context.addMockSystemService(DisplayManager::class.java, displayManager)
context.addMockSystemService(WindowManager::class.java, kosmos.windowManager)
-
`when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sideFpsView)
`when`(sideFpsView.requireViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
.thenReturn(mock(LottieAnimationView::class.java))
- with(mock(ViewPropertyAnimator::class.java)) {
- `when`(sideFpsView.animate()).thenReturn(this)
- `when`(alpha(Mockito.anyFloat())).thenReturn(this)
- `when`(setStartDelay(Mockito.anyLong())).thenReturn(this)
- `when`(setDuration(Mockito.anyLong())).thenReturn(this)
- `when`(setListener(any())).thenAnswer {
- (it.arguments[0] as Animator.AnimatorListener).onAnimationEnd(
- mock(Animator::class.java)
- )
- this
- }
- }
}
@Test
fun verifyIndicatorNotAdded_whenInRearDisplayMode() {
kosmos.testScope.runTest {
- setupTestConfiguration(
- DeviceConfig.X_ALIGNED,
- rotation = DisplayRotation.ROTATION_0,
- isInRearDisplayMode = true
- )
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.NotRunning
- )
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
- updatePrimaryBouncer(
- isShowing = true,
- isAnimatingAway = false,
- fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
- )
- runCurrent()
-
+ setupTestConfiguration(isInRearDisplayMode = true)
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
verify(kosmos.windowManager, never()).addView(any(), any())
}
}
@@ -168,33 +93,14 @@
@Test
fun verifyIndicatorShowAndHide_onPrimaryBouncerShowAndHide() {
kosmos.testScope.runTest {
- setupTestConfiguration(
- DeviceConfig.X_ALIGNED,
- rotation = DisplayRotation.ROTATION_0,
- isInRearDisplayMode = false
- )
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.NotRunning
- )
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
- // Show primary bouncer
- updatePrimaryBouncer(
- isShowing = true,
- isAnimatingAway = false,
- fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
- )
+ setupTestConfiguration(isInRearDisplayMode = false)
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
runCurrent()
verify(kosmos.windowManager).addView(any(), any())
// Hide primary bouncer
- updatePrimaryBouncer(
- isShowing = false,
- isAnimatingAway = false,
- fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
- )
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false)
runCurrent()
verify(kosmos.windowManager).removeView(any())
@@ -204,30 +110,19 @@
@Test
fun verifyIndicatorShowAndHide_onAlternateBouncerShowAndHide() {
kosmos.testScope.runTest {
- setupTestConfiguration(
- DeviceConfig.X_ALIGNED,
- rotation = DisplayRotation.ROTATION_0,
- isInRearDisplayMode = false
- )
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.NotRunning
- )
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
- // Show alternate bouncer
- kosmos.keyguardBouncerRepository.setAlternateVisible(true)
+ setupTestConfiguration(isInRearDisplayMode = false)
+ updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true)
runCurrent()
verify(kosmos.windowManager).addView(any(), any())
- var viewCaptor = argumentCaptor<View>()
verify(kosmos.windowManager).addView(viewCaptor.capture(), any())
verify(viewCaptor.firstValue)
.announceForAccessibility(
mContext.getText(R.string.accessibility_side_fingerprint_indicator_label)
)
- // Hide alternate bouncer
- kosmos.keyguardBouncerRepository.setAlternateVisible(false)
+ updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false)
runCurrent()
verify(kosmos.windowManager).removeView(any())
@@ -237,30 +132,14 @@
@Test
fun verifyIndicatorShownAndHidden_onSystemServerAuthenticationStartedAndStopped() {
kosmos.testScope.runTest {
- setupTestConfiguration(
- DeviceConfig.X_ALIGNED,
- rotation = DisplayRotation.ROTATION_0,
- isInRearDisplayMode = false
- )
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
- updatePrimaryBouncer(
- isShowing = false,
- isAnimatingAway = false,
- fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
- )
- // System server authentication started
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.BiometricPromptAuthentication
- )
+ setupTestConfiguration(isInRearDisplayMode = false)
+ updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
runCurrent()
verify(kosmos.windowManager).addView(any(), any())
// System server authentication stopped
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.NotRunning
- )
+ updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false)
runCurrent()
verify(kosmos.windowManager).removeView(any())
@@ -269,45 +148,35 @@
// On progress bar shown - hide indicator
// On progress bar hidden - show indicator
+ // TODO(b/365182034): update + enable when rest to unlock feature is implemented
+ @Ignore("b/365182034")
@Test
fun verifyIndicatorProgressBarInteraction() {
kosmos.testScope.runTest {
// Pre-auth conditions
- setupTestConfiguration(
- DeviceConfig.X_ALIGNED,
- rotation = DisplayRotation.ROTATION_0,
- isInRearDisplayMode = false
- )
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.NotRunning
- )
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
-
- // Show primary bouncer
- updatePrimaryBouncer(
- isShowing = true,
- isAnimatingAway = false,
- fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
- )
+ setupTestConfiguration(isInRearDisplayMode = false)
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
runCurrent()
val inOrder = inOrder(kosmos.windowManager)
-
// Verify indicator shown
inOrder.verify(kosmos.windowManager).addView(any(), any())
// Set progress bar visible
- kosmos.sideFpsProgressBarViewModel.setVisible(true)
-
+ // updateSfpsIndicatorRequests(
+ // kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
+ // true
+ // )
runCurrent()
// Verify indicator hidden
inOrder.verify(kosmos.windowManager).removeView(any())
// Set progress bar invisible
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
-
+ // updateSfpsIndicatorRequests(
+ // kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
+ // false
+ // )
runCurrent()
// Verify indicator shown
@@ -315,78 +184,18 @@
}
}
- private fun updatePrimaryBouncer(
- isShowing: Boolean,
- isAnimatingAway: Boolean,
- fpsDetectionRunning: Boolean,
- isUnlockingWithFpAllowed: Boolean,
- ) {
- kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
- kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
- val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
- kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
- primaryStartDisappearAnimation
- )
-
- whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
- .thenReturn(fpsDetectionRunning)
- whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
- .thenReturn(isUnlockingWithFpAllowed)
- mContext.orCreateTestableResources.addOverride(
- R.bool.config_show_sidefps_hint_on_bouncer,
- true
- )
- }
-
- private suspend fun TestScope.setupTestConfiguration(
- deviceConfig: DeviceConfig,
- rotation: DisplayRotation = DisplayRotation.ROTATION_0,
- isInRearDisplayMode: Boolean,
- ) {
- [email protected] = deviceConfig
-
- when (deviceConfig) {
- DeviceConfig.X_ALIGNED -> {
- displayWidth = 3000
- displayHeight = 1500
- boundsWidth = 200
- boundsHeight = 100
- sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2)
- }
- DeviceConfig.Y_ALIGNED -> {
- displayWidth = 2500
- displayHeight = 2000
- boundsWidth = 100
- boundsHeight = 200
- sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2)
- }
- }
-
- whenever(kosmos.windowManager.maximumWindowMetrics)
- .thenReturn(
- WindowMetrics(
- Rect(0, 0, displayWidth, displayHeight),
- mock(WindowInsets::class.java),
- )
- )
-
- contextDisplayInfo.uniqueId = DISPLAY_ID
-
+ private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) {
kosmos.fingerprintPropertyRepository.setProperties(
sensorId = 1,
strength = SensorStrength.STRONG,
sensorType = FingerprintSensorType.POWER_BUTTON,
- sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
+ sensorLocations = emptyMap()
)
kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
- kosmos.displayStateRepository.setCurrentRotation(rotation)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
kosmos.displayRepository.emitDisplayChangeEvent(0)
kosmos.sideFpsOverlayViewBinder.start()
runCurrent()
}
-
- companion object {
- private const val DISPLAY_ID = "displayId"
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index 0db7b62..27b1371 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -30,23 +30,19 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.model.KeyPath
-import com.android.keyguard.keyguardUpdateMonitor
import com.android.settingslib.Utils
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
-import com.android.systemui.biometrics.data.repository.biometricStatusRepository
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
-import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.LottieCallback
import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
+import com.android.systemui.biometrics.updateSfpsIndicatorRequests
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.displayStateRepository
-import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.testKosmos
@@ -284,17 +280,7 @@
kosmos.testScope.runTest {
val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.NotRunning
- )
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
-
- updatePrimaryBouncer(
- isShowing = true,
- isAnimatingAway = false,
- fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
- )
+ updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
runCurrent()
assertThat(lottieCallbacks)
@@ -312,17 +298,7 @@
val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
setDarkMode(true)
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.BiometricPromptAuthentication
- )
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
-
- updatePrimaryBouncer(
- isShowing = false,
- isAnimatingAway = false,
- fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
- )
+ updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
runCurrent()
assertThat(lottieCallbacks)
@@ -338,17 +314,7 @@
val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
setDarkMode(false)
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.BiometricPromptAuthentication
- )
- kosmos.sideFpsProgressBarViewModel.setVisible(false)
-
- updatePrimaryBouncer(
- isShowing = false,
- isAnimatingAway = false,
- fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
- )
+ updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
runCurrent()
assertThat(lottieCallbacks)
@@ -371,29 +337,6 @@
mContext.resources.configuration.uiMode = uiMode
}
- private fun updatePrimaryBouncer(
- isShowing: Boolean,
- isAnimatingAway: Boolean,
- fpsDetectionRunning: Boolean,
- isUnlockingWithFpAllowed: Boolean,
- ) {
- kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
- kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
- val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
- kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
- primaryStartDisappearAnimation
- )
-
- whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
- .thenReturn(fpsDetectionRunning)
- whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
- .thenReturn(isUnlockingWithFpAllowed)
- mContext.orCreateTestableResources.addOverride(
- R.bool.config_show_sidefps_hint_on_bouncer,
- true
- )
- }
-
private suspend fun TestScope.setupTestConfiguration(
deviceConfig: DeviceConfig,
rotation: DisplayRotation = DisplayRotation.ROTATION_0,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 65236f0..e3b5f34 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -31,6 +31,8 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
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
@@ -91,6 +93,8 @@
kosmos.fakeTelephonyRepository.setHasTelephonyRadio(true)
kosmos.telecomManager = telecomManager
+
+ kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "")
}
@Test
@@ -130,6 +134,7 @@
assertThat(metricsLogger.logs.element().category)
.isEqualTo(MetricsProto.MetricsEvent.ACTION_EMERGENCY_CALL)
verify(activityTaskManager).stopSystemLockTaskMode()
+ assertThat(kosmos.sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
verify(telecomManager).showInCallScreen(eq(false))
}
@@ -156,6 +161,7 @@
assertThat(metricsLogger.logs.element().category)
.isEqualTo(MetricsProto.MetricsEvent.ACTION_EMERGENCY_CALL)
verify(activityTaskManager).stopSystemLockTaskMode()
+ assertThat(kosmos.sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
// TODO(b/25189994): Test the activity has been started once we switch to the
// ActivityStarter interface here.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
index c161525..8c8faee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
@@ -19,9 +19,11 @@
import android.content.pm.UserInfo
import android.hardware.biometrics.BiometricFaceConstants
import android.hardware.fingerprint.FingerprintManager
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -35,7 +37,6 @@
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.shared.flag.fakeComposeBouncerFlags
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
@@ -71,6 +72,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableFlags(Flags.FLAG_COMPOSE_BOUNCER)
class BouncerMessageViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -82,7 +84,6 @@
@Before
fun setUp() {
kosmos.fakeUserRepository.setUserInfos(listOf(PRIMARY_USER))
- kosmos.fakeComposeBouncerFlags.composeBouncerEnabled = true
overrideResource(
R.array.config_face_acquire_device_entry_ignorelist,
intArrayOf(ignoreHelpMessageId)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
similarity index 93%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
index a86a0c0..f58bbc3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
@@ -44,17 +44,17 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableSceneContainer
-class BouncerSceneActionsViewModelTest : SysuiTestCase() {
+class BouncerUserActionsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private lateinit var underTest: BouncerSceneActionsViewModel
+ private lateinit var underTest: BouncerUserActionsViewModel
@Before
fun setUp() {
kosmos.sceneContainerStartable.start()
- underTest = kosmos.bouncerSceneActionsViewModel
+ underTest = kosmos.bouncerUserActionsViewModel
underTest.activateIn(testScope)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index ec8cc4d..956c129 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -34,8 +34,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -83,7 +81,6 @@
private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
private final FalsingClassifier.Result mFalsedResult =
FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), "");
- private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
@Before
public void setup() {
@@ -98,11 +95,11 @@
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mFalsingDataProvider.isUnfolded()).thenReturn(false);
+ when(mFalsingDataProvider.isTouchScreenSource()).thenReturn(true);
mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
mMetricsLogger, mClassifiers, mSingleTapClassifier, mLongTapClassifier,
mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
- mAccessibilityManager, false, mFakeFeatureFlags);
- mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
+ mAccessibilityManager, false);
}
@Test
@@ -197,6 +194,13 @@
}
@Test
+ public void testSkipNonTouchscreenDevices() {
+ assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+ when(mFalsingDataProvider.isTouchScreenSource()).thenReturn(false);
+ assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+ }
+
+ @Test
public void testTrackpadGesture() {
assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
similarity index 81%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index 49c6239..df4b048 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -25,13 +26,20 @@
import static org.mockito.Mockito.when;
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManagerGlobal;
+import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.util.DisplayMetrics;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -56,11 +64,15 @@
private FoldStateListener mFoldStateListener;
private final DockManagerFake mDockManager = new DockManagerFake();
private DisplayMetrics mDisplayMetrics;
+ private IInputManager mIInputManager;
+ private InputManagerGlobal.TestSession inputManagerGlobalTestSession;
@Before
public void setup() {
super.setup();
MockitoAnnotations.initMocks(this);
+ mIInputManager = mock(IInputManager.Stub.class);
+ inputManagerGlobalTestSession = InputManagerGlobal.createTestSession(mIInputManager);
mDisplayMetrics = new DisplayMetrics();
mDisplayMetrics.xdpi = 100;
mDisplayMetrics.ydpi = 100;
@@ -73,6 +85,7 @@
public void tearDown() {
super.tearDown();
mDataProvider.onSessionEnd();
+ inputManagerGlobalTestSession.close();
}
@Test
@@ -378,6 +391,79 @@
}
@Test
+ @DisableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_flagOff_alwaysTrue() {
+ assertThat(mDataProvider.isTouchScreenSource()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_recentEventsEmpty_true() {
+ //send no events into the data provider
+ assertThat(mDataProvider.isTouchScreenSource()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_latestDeviceTouchscreen_true() throws RemoteException {
+ int deviceId = 999;
+
+ InputDevice device = new InputDevice.Builder()
+ .setSources(InputDevice.SOURCE_CLASS_TRACKBALL | InputDevice.SOURCE_TOUCHSCREEN)
+ .setId(deviceId)
+ .build();
+ when(mIInputManager.getInputDeviceIds()).thenReturn(new int[]{deviceId});
+ when(mIInputManager.getInputDevice(anyInt())).thenReturn(device);
+
+ MotionEvent event = MotionEvent.obtain(1, 0, MotionEvent.ACTION_UP, 1,
+ MotionEvent.PointerProperties.createArray(1),
+ MotionEvent.PointerCoords.createArray(1), 0, 0, 1.0f, 1.0f, deviceId, 0,
+ InputDevice.SOURCE_CLASS_NONE, 0, 0, 0);
+
+ mDataProvider.onMotionEvent(event);
+ boolean result = mDataProvider.isTouchScreenSource();
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_latestDeviceNonTouchscreen_false() throws RemoteException {
+ int deviceId = 9999;
+
+ InputDevice device = new InputDevice.Builder()
+ .setSources(InputDevice.SOURCE_CLASS_TRACKBALL)
+ .setId(deviceId)
+ .build();
+ when(mIInputManager.getInputDeviceIds()).thenReturn(new int[]{deviceId});
+ when(mIInputManager.getInputDevice(anyInt())).thenReturn(device);
+
+ MotionEvent event = MotionEvent.obtain(1, 0, MotionEvent.ACTION_UP, 1,
+ MotionEvent.PointerProperties.createArray(1),
+ MotionEvent.PointerCoords.createArray(1), 0, 0, 1.0f, 1.0f, deviceId, 0,
+ InputDevice.SOURCE_CLASS_NONE, 0, 0, 0);
+
+ mDataProvider.onMotionEvent(event);
+ boolean result = mDataProvider.isTouchScreenSource();
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_latestDeviceNull_true() {
+ // Do not mock InputManager for this test
+ inputManagerGlobalTestSession.close();
+
+ int nonExistentDeviceId = 9997;
+ MotionEvent event = MotionEvent.obtain(1, 0, MotionEvent.ACTION_UP, 1,
+ MotionEvent.PointerProperties.createArray(1),
+ MotionEvent.PointerCoords.createArray(1), 0, 0, 1.0f, 1.0f, nonExistentDeviceId, 0,
+ InputDevice.SOURCE_CLASS_NONE, 0, 0, 0);
+
+ mDataProvider.onMotionEvent(event);
+ assertThat(mDataProvider.isTouchScreenSource()).isTrue();
+ }
+
+ @Test
public void test_UnfoldedState_Folded() {
FalsingDataProvider falsingDataProvider = createWithFoldCapability(true);
when(mFoldStateListener.getFolded()).thenReturn(true);
@@ -413,7 +499,7 @@
}
private FalsingDataProvider createWithFoldCapability(boolean foldable) {
- return new FalsingDataProvider(
- mDisplayMetrics, mBatteryController, mFoldStateListener, mDockManager, foldable);
+ return new FalsingDataProvider(mDisplayMetrics, mBatteryController, mFoldStateListener,
+ mDockManager, foldable);
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
index bb400f2..f06cd6a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -67,7 +67,8 @@
isAttachedToWindow = { isAttachedToWindow },
onLongPressDetected = onLongPressDetected,
onSingleTapDetected = onSingleTapDetected,
- longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() }
+ longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() },
+ allowedTouchSlop = ViewConfiguration.getTouchSlop(),
)
underTest.isLongPressHandlingEnabled = true
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
index 3b0057d..e531e65 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
@@ -22,6 +22,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -73,6 +74,7 @@
keyguardInteractor = kosmos.keyguardInteractor,
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
dreamManager = dreamManager,
+ communalSceneInteractor = kosmos.communalSceneInteractor,
bgScope = kosmos.applicationCoroutineScope,
)
.apply { start() }
@@ -158,6 +160,36 @@
}
}
+ @Test
+ fun shouldNotStartDreamWhenLaunchingWidget() =
+ testScope.runTest {
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setDreaming(false)
+ powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
+ kosmos.communalSceneInteractor.setIsLaunchingWidget(true)
+ whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true)
+ runCurrent()
+
+ transition(from = KeyguardState.DREAMING, to = KeyguardState.GLANCEABLE_HUB)
+
+ verify(dreamManager, never()).startDream()
+ }
+
+ @Test
+ fun shouldNotStartDreamWhenOccluded() =
+ testScope.runTest {
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setDreaming(false)
+ powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
+ keyguardRepository.setKeyguardOccluded(true)
+ whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true)
+ runCurrent()
+
+ transition(from = KeyguardState.DREAMING, to = KeyguardState.GLANCEABLE_HUB)
+
+ verify(dreamManager, never()).startDream()
+ }
+
private suspend fun TestScope.transition(from: KeyguardState, to: KeyguardState) {
kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
from = from,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
index c37b33e..ae1c496 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
@@ -29,7 +29,7 @@
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,8 +45,7 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
-
- private lateinit var secureSettings: FakeSettings
+ private val secureSettings = kosmos.fakeSettings
private lateinit var userRepository: FakeUserRepository
private lateinit var underTest: CommunalTutorialRepositoryImpl
@@ -55,7 +54,6 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- secureSettings = FakeSettings()
userRepository = FakeUserRepository()
val listOfUserInfo = listOf(MAIN_USER_INFO)
userRepository.setUserInfos(listOfUserInfo)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 99fcbb8..777ddab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -68,15 +68,16 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.statusbar.phone.fakeManagedProfileController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.leaks.FakeManagedProfileController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -122,6 +123,7 @@
private lateinit var userTracker: FakeUserTracker
private lateinit var activityStarter: ActivityStarter
private lateinit var userManager: UserManager
+ private lateinit var managedProfileController: FakeManagedProfileController
private lateinit var underTest: CommunalInteractor
@@ -143,6 +145,7 @@
userTracker = kosmos.fakeUserTracker
activityStarter = kosmos.activityStarter
userManager = kosmos.userManager
+ managedProfileController = kosmos.fakeManagedProfileController
whenever(mainUser.isMain).thenReturn(true)
whenever(secondaryUser.isMain).thenReturn(false)
@@ -1070,6 +1073,14 @@
}
}
+ @Test
+ fun unpauseWorkProfileEnablesWorkMode() =
+ testScope.runTest {
+ underTest.unpauseWorkProfile()
+
+ assertThat(managedProfileController.isWorkModeEnabled()).isTrue()
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 3e75ceb..71abed7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -75,7 +75,7 @@
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.log.SessionTracker
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
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
@@ -90,12 +90,12 @@
import com.android.systemui.util.mockito.captureMany
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -198,25 +198,8 @@
fmOverride: FaceManager? = faceManager,
bypassControllerOverride: KeyguardBypassController? = bypassController
): DeviceEntryFaceAuthRepositoryImpl {
- val systemClock = FakeSystemClock()
- val faceAuthBuffer =
- TableLogBuffer(
- 10,
- "face auth",
- systemClock,
- mock(),
- testDispatcher,
- testScope.backgroundScope
- )
- val faceDetectBuffer =
- TableLogBuffer(
- 10,
- "face detect",
- systemClock,
- mock(),
- testDispatcher,
- testScope.backgroundScope
- )
+ val faceAuthBuffer = logcatTableLogBuffer(kosmos, "face auth")
+ val faceDetectBuffer = logcatTableLogBuffer(kosmos, "face detect")
return DeviceEntryFaceAuthRepositoryImpl(
mContext,
@@ -561,14 +544,29 @@
fun withSceneContainerEnabled_authenticateDoesNotRunWhenKeyguardIsGoingAway() =
testScope.runTest {
testGatingCheckForFaceAuth(sceneContainerEnabled = true) {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- KeyguardState.LOCKSCREEN,
- KeyguardState.UNDEFINED,
- value = 0.5f,
- transitionState = TransitionState.RUNNING
- ),
- validateStep = false
+ kosmos.sceneInteractor.setTransitionState(
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Bouncer,
+ toScene = Scenes.Gone,
+ currentScene = flowOf(Scenes.Bouncer),
+ progress = MutableStateFlow(0.2f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ )
+ runCurrent()
+ }
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun withSceneContainerEnabled_authenticateDoesNotRunWhenLockscreenIsGone() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth(sceneContainerEnabled = true) {
+ kosmos.sceneInteractor.setTransitionState(
+ MutableStateFlow(ObservableTransitionState.Idle(Scenes.Gone))
)
runCurrent()
}
@@ -898,15 +896,32 @@
fun withSceneContainer_faceDetectDoesNotRunWhenKeyguardGoingAway() =
testScope.runTest {
testGatingCheckForDetect(sceneContainerEnabled = true) {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- KeyguardState.LOCKSCREEN,
- KeyguardState.UNDEFINED,
- value = 0.5f,
- transitionState = TransitionState.RUNNING
- ),
- validateStep = false
+ kosmos.sceneInteractor.setTransitionState(
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Bouncer,
+ toScene = Scenes.Gone,
+ currentScene = flowOf(Scenes.Bouncer),
+ progress = MutableStateFlow(0.2f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
)
+
+ runCurrent()
+ }
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun withSceneContainer_faceDetectDoesNotRunWhenLockscreenIsGone() =
+ testScope.runTest {
+ testGatingCheckForDetect(sceneContainerEnabled = true) {
+ kosmos.sceneInteractor.setTransitionState(
+ MutableStateFlow(ObservableTransitionState.Idle(Scenes.Gone))
+ )
+
runCurrent()
}
}
@@ -1231,6 +1246,9 @@
TransitionStep(KeyguardState.OFF, KeyguardState.LOCKSCREEN, value = 1.0f),
validateStep = false
)
+ kosmos.sceneInteractor.setTransitionState(
+ MutableStateFlow(ObservableTransitionState.Idle(Scenes.Lockscreen))
+ )
} else {
keyguardRepository.setKeyguardGoingAway(false)
}
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 d21a827..7dd7174 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -233,7 +233,7 @@
.thenReturn(dreamOverlayComponent)
val ambientTouchComponent = mock<AmbientTouchComponent>()
- whenever(ambientTouchComponentFactory.create(any(), any()))
+ whenever(ambientTouchComponentFactory.create(any(), any(), any()))
.thenReturn(ambientTouchComponent)
whenever(ambientTouchComponent.getTouchMonitor()).thenReturn(mTouchMonitor)
@@ -1139,6 +1139,76 @@
}
@Test
+ fun testDreamActivityGesturesNotBlockedWhenNotificationShadeShowing() {
+ 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()
+
+ 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 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/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index ca15eff..25c5336 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -24,8 +24,10 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.contextualeducation.GestureType.BACK
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.education.data.model.GestureEduModel
import com.android.systemui.education.data.repository.contextualEducationRepository
import com.android.systemui.education.data.repository.fakeEduClock
@@ -61,6 +63,8 @@
private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
private val eduClock = kosmos.fakeEduClock
+ private val minDurationForNextEdu =
+ KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
@Before
fun setup() {
@@ -92,7 +96,10 @@
triggerMaxEducationSignals(BACK)
// runCurrent() to trigger 1st education
runCurrent()
+
+ eduClock.offset(minDurationForNextEdu)
triggerMaxEducationSignals(BACK)
+
assertThat(model?.educationUiType).isEqualTo(EducationUiType.Notification)
}
@@ -114,6 +121,39 @@
}
@Test
+ fun no2ndEducationBeforeMinEduIntervalReached() =
+ testScope.runTest {
+ val models by collectValues(underTest.educationTriggered)
+ triggerMaxEducationSignals(BACK)
+ runCurrent()
+
+ // Offset a duration that is less than the required education interval
+ eduClock.offset(1.seconds)
+ triggerMaxEducationSignals(BACK)
+ runCurrent()
+
+ assertThat(models.filterNotNull().size).isEqualTo(1)
+ }
+
+ @Test
+ fun noNewEducationInfoAfterMaxEducationCountReached() =
+ testScope.runTest {
+ val models by collectValues(underTest.educationTriggered)
+ // Trigger 2 educations
+ triggerMaxEducationSignals(BACK)
+ runCurrent()
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(BACK)
+ runCurrent()
+
+ // Try triggering 3rd education
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(BACK)
+
+ assertThat(models.filterNotNull().size).isEqualTo(2)
+ }
+
+ @Test
fun startNewUsageSessionWhen2ndSignalReceivedAfterSessionDeadline() =
testScope.runTest {
val model by
@@ -220,17 +260,19 @@
verify(kosmos.mockEduInputManager)
.registerKeyGestureEventListener(any(), listenerCaptor.capture())
- val backGestureEvent =
+ val allAppsKeyGestureEvent =
KeyGestureEvent(
/* deviceId= */ 1,
- intArrayOf(KeyEvent.KEYCODE_ESCAPE),
+ IntArray(0),
KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS
)
- listenerCaptor.value.onKeyGestureEvent(backGestureEvent)
+ listenerCaptor.value.onKeyGestureEvent(allAppsKeyGestureEvent)
val model by
- collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK))
+ collectLastValue(
+ kosmos.contextualEducationRepository.readGestureEduModelFlow(ALL_APPS)
+ )
assertThat(model?.lastShortcutTriggeredTime).isEqualTo(eduClock.instant())
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
index cd0c58f..98e0947 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
@@ -19,14 +19,22 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.education.data.repository.contextualEducationRepository
import com.android.systemui.education.data.repository.fakeEduClock
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
+import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.data.repository.touchpadRepository
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,24 +44,129 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val underTest = kosmos.keyboardTouchpadEduStatsInteractor
+ private val keyboardRepository = kosmos.keyboardRepository
+ private val touchpadRepository = kosmos.touchpadRepository
+ private val repository = kosmos.contextualEducationRepository
+ private val fakeClock = kosmos.fakeEduClock
+ private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
+ private val initialDelayElapsedDuration =
+ KeyboardTouchpadEduStatsInteractorImpl.initialDelayDuration + 1.seconds
@Test
- fun dataUpdatedOnIncrementSignalCount() =
+ fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() =
testScope.runTest {
- val model by
- collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK))
+ setUpForInitialDelayElapse()
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
val originalValue = model!!.signalCount
underTest.incrementSignalCount(BACK)
+
assertThat(model?.signalCount).isEqualTo(originalValue + 1)
}
@Test
+ fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() =
+ testScope.runTest {
+ setUpForInitialDelayElapse()
+ touchpadRepository.setIsAnyTouchpadConnected(false)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(BACK)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() =
+ testScope.runTest {
+ setUpForInitialDelayElapse()
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(ALL_APPS)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() =
+ testScope.runTest {
+ setUpForInitialDelayElapse()
+ keyboardRepository.setIsAnyKeyboardConnected(false)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(ALL_APPS)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
fun dataAddedOnUpdateShortcutTriggerTime() =
testScope.runTest {
- val model by
- collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK))
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
assertThat(model?.lastShortcutTriggeredTime).isNull()
underTest.updateShortcutTriggerTime(BACK)
assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
}
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() =
+ testScope.runTest {
+ setUpForDeviceConnection()
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant())
+
+ fakeClock.offset(initialDelayElapsedDuration)
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(BACK)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() =
+ testScope.runTest {
+ setUpForDeviceConnection()
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant())
+
+ // No offset to the clock to simulate update before initial delay
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(BACK)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() =
+ testScope.runTest {
+ // No update to OOBE launch time to simulate no OOBE is launched yet
+ setUpForDeviceConnection()
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(BACK)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ private suspend fun setUpForInitialDelayElapse() {
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant())
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, fakeClock.instant())
+ fakeClock.offset(initialDelayElapsedDuration)
+ }
+
+ private fun setUpForDeviceConnection() {
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+ }
+
+ @After
+ fun clear() {
+ testScope.launch { tutorialSchedulerRepository.clearDataStore() }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
index e075b7e..c4ac585 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.education.data.repository.fakeEduClock
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
import com.android.systemui.education.domain.interactor.contextualEducationInteractor
import com.android.systemui.education.domain.interactor.keyboardTouchpadEduInteractor
@@ -35,6 +36,7 @@
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -56,6 +58,9 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val interactor = kosmos.contextualEducationInteractor
+ private val eduClock = kosmos.fakeEduClock
+ private val minDurationForNextEdu =
+ KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
private lateinit var underTest: ContextualEduUiCoordinator
@Mock private lateinit var toast: Toast
@Mock private lateinit var notificationManager: NotificationManager
@@ -94,6 +99,7 @@
fun showNotificationOn2ndEdu() =
testScope.runTest {
triggerEducation(BACK)
+ eduClock.offset(minDurationForNextEdu)
triggerEducation(BACK)
verify(notificationManager).notifyAsUser(any(), anyInt(), any(), any())
}
@@ -110,7 +116,10 @@
testScope.runTest {
val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
triggerEducation(BACK)
+
+ eduClock.offset(minDurationForNextEdu)
triggerEducation(BACK)
+
verify(notificationManager)
.notifyAsUser(any(), anyInt(), notificationCaptor.capture(), any())
verifyNotificationContent(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index fd4ed38..686b518 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -23,7 +23,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.haptics.fakeVibratorHelper
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.qs.qsTileFactory
@@ -50,7 +50,7 @@
@Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
private val kosmos = testKosmos()
- private val vibratorHelper = kosmos.vibratorHelper
+ private val vibratorHelper = kosmos.fakeVibratorHelper
private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
@Mock private lateinit var callback: QSLongPressEffect.Callback
@Mock private lateinit var controller: ActivityTransitionAnimator.Controller
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index bbfaf6f..6c3c7ef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -31,19 +31,19 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -63,27 +63,24 @@
@RunWith(AndroidJUnit4::class)
class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@Mock private lateinit var zenModeController: ZenModeController
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var conditionUri: Uri
@Mock private lateinit var enableZenModeDialog: EnableZenModeDialog
@Captor private lateinit var spyZenMode: ArgumentCaptor<Int>
@Captor private lateinit var spyConditionId: ArgumentCaptor<Uri?>
- private lateinit var settings: FakeSettings
private lateinit var underTest: DoNotDisturbQuickAffordanceConfig
- private lateinit var testDispatcher: TestDispatcher
- private lateinit var testScope: TestScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- testDispatcher = StandardTestDispatcher()
- testScope = TestScope(testDispatcher)
-
- settings = FakeSettings()
-
underTest =
DoNotDisturbQuickAffordanceConfig(
context,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 26fcb23..0145f17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -23,19 +23,19 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -51,14 +51,15 @@
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.unconfinedTestDispatcher
+ private val testScope = kosmos.unconfinedTestScope
+ private val settings = kosmos.unconfinedDispatcherFakeSettings
+
@Mock private lateinit var sharedPrefs: FakeSharedPreferences
private lateinit var underTest: KeyguardQuickAffordanceLegacySettingSyncer
-
- private lateinit var testScope: TestScope
- private lateinit var testDispatcher: TestDispatcher
private lateinit var selectionManager: KeyguardQuickAffordanceLocalUserSelectionManager
- private lateinit var settings: FakeSettings
@Before
fun setUp() {
@@ -73,8 +74,6 @@
whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)).thenReturn(true)
whenever(context.resources).thenReturn(resources)
- testDispatcher = UnconfinedTestDispatcher()
- testScope = TestScope(testDispatcher)
selectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
@@ -92,7 +91,6 @@
userTracker = FakeUserTracker(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
- settings = FakeSettings()
settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0)
settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_WALLET, 0)
settings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index c5ba02d..4e429c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -46,11 +46,10 @@
import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT
import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
import com.android.systemui.keyguard.shared.model.DevicePosture
-import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.res.R
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
@@ -81,6 +80,8 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
class BiometricSettingsRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var underTest: BiometricSettingsRepository
@Mock private lateinit var authController: AuthController
@@ -88,7 +89,6 @@
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var biometricManager: BiometricManager
- @Mock private lateinit var tableLogger: TableLogBuffer
@Captor
private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@@ -99,7 +99,7 @@
private lateinit var devicePostureRepository: FakeDevicePostureRepository
private lateinit var facePropertyRepository: FakeFacePropertyRepository
private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
- private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
+ private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -115,8 +115,6 @@
devicePostureRepository = FakeDevicePostureRepository()
facePropertyRepository = FakeFacePropertyRepository()
fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
- mobileConnectionsRepository =
- FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
}
private suspend fun createBiometricSettingsRepository() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index c85cd66..1582e47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -31,20 +31,21 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
@@ -58,6 +59,11 @@
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
private lateinit var underTest: KeyguardQuickAffordanceRepository
private lateinit var config1: FakeKeyguardQuickAffordanceConfig
@@ -65,7 +71,6 @@
private lateinit var userTracker: FakeUserTracker
private lateinit var client1: FakeCustomizationProviderClient
private lateinit var client2: FakeCustomizationProviderClient
- private lateinit var testScope: TestScope
@Before
fun setUp() {
@@ -73,8 +78,6 @@
context.resources.configuration.setLayoutDirection(Locale.US)
config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
- val testDispatcher = StandardTestDispatcher()
- testScope = TestScope(testDispatcher)
userTracker = FakeUserTracker()
val localUserSelectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
@@ -128,7 +131,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = settings,
selectionsManager = localUserSelectionManager,
),
configs = setOf(config1, config2),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index ad07c1c..a8bb2b0a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -61,7 +61,7 @@
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -80,6 +80,10 @@
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var userTracker: UserTracker
@@ -90,11 +94,8 @@
@Mock private lateinit var logger: KeyguardQuickAffordancesLogger
@Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
- private val kosmos = testKosmos()
-
private lateinit var underTest: KeyguardQuickAffordanceInteractor
- private val testScope = kosmos.testScope
private lateinit var repository: FakeKeyguardRepository
private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
@@ -170,7 +171,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = kosmos.testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = settings,
selectionsManager = localUserSelectionManager,
),
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 6708ffa..12039c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -1394,9 +1394,11 @@
kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ sendSteps(sendStep1)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
- sendSteps(sendStep1, sendStep2, sendStep3)
+ sendSteps(sendStep2, sendStep3)
assertEquals(listOf(sendStep1, sendStep2), currentStates)
assertEquals(listOf(sendStep1, sendStep2), currentStatesConverted)
@@ -1410,6 +1412,7 @@
kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
sendSteps(sendStep1, sendStep2, sendStep3)
@@ -1426,6 +1429,7 @@
kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
sendSteps(sendStep1, sendStep2, sendStep3)
@@ -1443,6 +1447,7 @@
kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
@@ -1461,10 +1466,12 @@
kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ sendSteps(sendStep1)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
- sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+ sendSteps(sendStep2, sendStep3, sendStep4)
assertEquals(listOf(sendStep1, sendStep2), currentStates)
assertEquals(listOf(sendStep1, sendStep2), currentStatesMapped)
@@ -1478,10 +1485,12 @@
kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ sendSteps(sendStep1)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
- sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+ sendSteps(sendStep2, sendStep3, sendStep4)
assertEquals(listOf<TransitionStep>(), currentStatesMapped)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 3e1f4f6..3b2b12c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -360,6 +360,7 @@
}
@Test
+ @DisableSceneContainer
fun alpha_transitionBetweenHubAndDream_isZero() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha(viewState))
@@ -388,8 +389,8 @@
ObservableTransitionState.Transition(
fromScene = Scenes.Lockscreen,
toScene = Scenes.Communal,
- emptyFlow(),
- emptyFlow(),
+ flowOf(Scenes.Communal),
+ flowOf(0.5f),
false,
emptyFlow()
)
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/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
index 86b3f33..0a0ded7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -85,26 +86,22 @@
testScope.runTest {
fingerprintPropertyRepository.supportsUdfps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
- listOf(
- step(0f, TransitionState.STARTED),
- step(0f),
- step(0.1f),
- step(0.2f),
- step(0.3f),
- step(1f),
- ),
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
testScope,
)
- values.forEach { assertThat(it).isEqualTo(0f) }
+ assertThat(values[0]).isEqualTo(1f)
+ assertThat(values[1]).isEqualTo(0f)
}
-
@Test
- fun lockscreenAlphaFadesOutAndFinishesVisible() =
+ fun lockscreenAlphaDoesNotFadeOut() =
testScope.runTest {
val alpha by collectValues(underTest.lockscreenAlpha)
keyguardTransitionRepository.sendTransitionSteps(
@@ -113,31 +110,7 @@
testScope,
)
- assertThat(alpha[0]).isEqualTo(1f)
- // Halfway through, it will have faded out
- assertThat(alpha[1]).isEqualTo(0f)
- // FINISHED alpha should be visible, to support pulsing
- assertThat(alpha[2]).isEqualTo(1f)
- }
-
- @Test
- fun deviceEntryBackgroundViewDisappear() =
- testScope.runTest {
- val values by collectValues(underTest.deviceEntryBackgroundViewAlpha)
-
- keyguardTransitionRepository.sendTransitionSteps(
- listOf(
- step(0f, TransitionState.STARTED),
- step(0f),
- step(0.1f),
- step(0.2f),
- step(0.3f),
- step(1f),
- ),
- testScope,
- )
-
- values.forEach { assertThat(it).isEqualTo(0f) }
+ alpha.forEach { assertThat(it).isEqualTo(1f) }
}
private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
similarity index 64%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
index c66ebf3..a330cf0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
@@ -27,6 +28,7 @@
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -42,8 +44,10 @@
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.TransitionKeys
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.testKosmos
@@ -63,7 +67,7 @@
@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper
@EnableSceneContainer
-class LockscreenSceneActionsViewModelTest : SysuiTestCase() {
+class LockscreenUserActionsViewModelTest : SysuiTestCase() {
companion object {
private const val parameterCount = 6
@@ -111,12 +115,12 @@
private fun expectedDownDestination(
downFromEdge: Boolean,
- isSingleShade: Boolean,
+ isNarrowScreen: Boolean,
isShadeTouchable: Boolean,
): SceneKey? {
return when {
!isShadeTouchable -> null
- downFromEdge && isSingleShade -> Scenes.QuickSettings
+ downFromEdge && isNarrowScreen -> Scenes.QuickSettings
else -> Scenes.Shade
}
}
@@ -162,7 +166,7 @@
@JvmField @Parameter(0) var canSwipeToEnter: Boolean = false
@JvmField @Parameter(1) var downWithTwoPointers: Boolean = false
@JvmField @Parameter(2) var downFromEdge: Boolean = false
- @JvmField @Parameter(3) var isSingleShade: Boolean = true
+ @JvmField @Parameter(3) var isNarrowScreen: Boolean = true
@JvmField @Parameter(4) var isCommunalAvailable: Boolean = false
@JvmField @Parameter(5) var isShadeTouchable: Boolean = false
@@ -170,7 +174,8 @@
@Test
@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
- fun destinationScenes() =
+ @DisableFlags(Flags.FLAG_DUAL_SHADE)
+ fun userActions_fullscreenShade() =
testScope.runTest {
underTest.activateIn(this)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
@@ -182,7 +187,7 @@
}
)
sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- kosmos.shadeRepository.setShadeLayoutWide(!isSingleShade)
+ kosmos.shadeRepository.setShadeLayoutWide(!isNarrowScreen)
kosmos.setCommunalAvailable(isCommunalAvailable)
kosmos.fakePowerRepository.updateWakefulness(
rawState =
@@ -190,12 +195,12 @@
WakefulnessState.AWAKE
} else {
WakefulnessState.ASLEEP
- },
+ }
)
- val destinationScenes by collectLastValue(underTest.actions)
+ val userActions by collectLastValue(underTest.actions)
val downDestination =
- destinationScenes?.get(
+ userActions?.get(
Swipe(
SwipeDirection.Down,
fromSource = Edge.Top.takeIf { downFromEdge },
@@ -212,7 +217,7 @@
.isEqualTo(
expectedDownDestination(
downFromEdge = downFromEdge,
- isSingleShade = isSingleShade,
+ isNarrowScreen = isNarrowScreen,
isShadeTouchable = isShadeTouchable,
)
)
@@ -220,18 +225,17 @@
assertThat(downDestination?.transitionKey)
.isEqualTo(
expectedDownTransitionKey(
- isSingleShade = isSingleShade,
+ isSingleShade = isNarrowScreen,
isShadeTouchable = isShadeTouchable,
)
)
val upScene by
collectLastValue(
- (destinationScenes?.get(Swipe(SwipeDirection.Up))
- as? UserActionResult.ChangeScene)
- ?.toScene
- ?.let { scene -> kosmos.sceneInteractor.resolveSceneFamily(scene) }
- ?: flowOf(null)
+ (userActions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene?.let {
+ scene ->
+ kosmos.sceneInteractor.resolveSceneFamily(scene)
+ } ?: flowOf(null)
)
assertThat(upScene)
@@ -244,11 +248,10 @@
val leftScene by
collectLastValue(
- (destinationScenes?.get(Swipe(SwipeDirection.Left))
- as? UserActionResult.ChangeScene)
- ?.toScene
- ?.let { scene -> kosmos.sceneInteractor.resolveSceneFamily(scene) }
- ?: flowOf(null)
+ (userActions?.get(Swipe.Left) as? UserActionResult.ChangeScene)?.toScene?.let {
+ scene ->
+ kosmos.sceneInteractor.resolveSceneFamily(scene)
+ } ?: flowOf(null)
)
assertThat(leftScene)
@@ -260,8 +263,103 @@
)
}
- private fun createLockscreenSceneViewModel(): LockscreenSceneActionsViewModel {
- return LockscreenSceneActionsViewModel(
+ @Test
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_DUAL_SHADE)
+ fun userActions_dualShade() =
+ testScope.runTest {
+ underTest.activateIn(this)
+ kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ if (canSwipeToEnter) {
+ AuthenticationMethodModel.None
+ } else {
+ AuthenticationMethodModel.Pin
+ }
+ )
+ sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+ kosmos.shadeRepository.setShadeLayoutWide(!isNarrowScreen)
+ kosmos.setCommunalAvailable(isCommunalAvailable)
+ kosmos.fakePowerRepository.updateWakefulness(
+ rawState =
+ if (isShadeTouchable) {
+ WakefulnessState.AWAKE
+ } else {
+ WakefulnessState.ASLEEP
+ }
+ )
+
+ val userActions by collectLastValue(underTest.actions)
+
+ val downDestination =
+ userActions?.get(
+ Swipe(
+ SwipeDirection.Down,
+ fromSource = Edge.Top.takeIf { downFromEdge },
+ pointerCount = if (downWithTwoPointers) 2 else 1,
+ )
+ )
+
+ if (downFromEdge || downWithTwoPointers || !isShadeTouchable) {
+ // Top edge is not applicable in dual shade, as well as two-finger swipe.
+ assertThat(downDestination).isNull()
+ } else {
+ assertThat(downDestination).isEqualTo(ShowOverlay(Overlays.NotificationsShade))
+ assertThat(downDestination?.transitionKey).isNull()
+ }
+
+ val downFromTopRightDestination =
+ userActions?.get(
+ Swipe(
+ SwipeDirection.Down,
+ fromSource = SceneContainerEdge.TopRight,
+ pointerCount = if (downWithTwoPointers) 2 else 1,
+ )
+ )
+ when {
+ !isShadeTouchable -> assertThat(downFromTopRightDestination).isNull()
+ downWithTwoPointers -> assertThat(downFromTopRightDestination).isNull()
+ else -> {
+ assertThat(downFromTopRightDestination)
+ .isEqualTo(ShowOverlay(Overlays.QuickSettingsShade))
+ assertThat(downFromTopRightDestination?.transitionKey).isNull()
+ }
+ }
+
+ val upScene by
+ collectLastValue(
+ (userActions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene?.let {
+ scene ->
+ kosmos.sceneInteractor.resolveSceneFamily(scene)
+ } ?: flowOf(null)
+ )
+
+ assertThat(upScene)
+ .isEqualTo(
+ expectedUpDestination(
+ canSwipeToEnter = canSwipeToEnter,
+ isShadeTouchable = isShadeTouchable,
+ )
+ )
+
+ val leftScene by
+ collectLastValue(
+ (userActions?.get(Swipe.Left) as? UserActionResult.ChangeScene)?.toScene?.let {
+ scene ->
+ kosmos.sceneInteractor.resolveSceneFamily(scene)
+ } ?: flowOf(null)
+ )
+
+ assertThat(leftScene)
+ .isEqualTo(
+ expectedLeftDestination(
+ isCommunalAvailable = isCommunalAvailable,
+ isShadeTouchable = isShadeTouchable,
+ )
+ )
+ }
+
+ private fun createLockscreenSceneViewModel(): LockscreenUserActionsViewModel {
+ return LockscreenUserActionsViewModel(
deviceEntryInteractor = kosmos.deviceEntryInteractor,
communalInteractor = kosmos.communalInteractor,
shadeInteractor = kosmos.shadeInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
index 22e5896..69ccc58 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
@@ -34,7 +34,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
-import com.android.systemui.flags.Flags.MEDIA_SESSION_ACTIONS
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.graphics.imageLoader
import com.android.systemui.kosmos.testDispatcher
@@ -42,7 +41,6 @@
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.fakeMediaControllerFactory
import com.android.systemui.media.controls.util.mediaFlags
-import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.testKosmos
@@ -87,7 +85,6 @@
context,
testDispatcher,
testScope,
- kosmos.activityStarter,
mediaControllerFactory,
mediaFlags,
kosmos.imageLoader,
@@ -96,7 +93,6 @@
@Before
fun setUp() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
mediaControllerFactory.setControllerForToken(session.sessionToken, mediaController)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
new file mode 100644
index 0000000..f6865f13
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.notifications.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayActionsViewModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val underTest = kosmos.notificationsShadeOverlayActionsViewModel
+
+ @Test
+ fun upTransitionSceneKey_hidesShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+ underTest.activateIn(this)
+
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+ .isEqualTo(Overlays.NotificationsShade)
+ assertThat(actions?.get(Swipe.Down)).isNull()
+ }
+
+ @Test
+ fun back_hidesShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+ underTest.activateIn(this)
+
+ assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+ .isEqualTo(Overlays.NotificationsShade)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
new file mode 100644
index 0000000..88a1df1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.notifications.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val sceneInteractor = kosmos.sceneInteractor
+
+ private val underTest = kosmos.notificationsShadeOverlayContentViewModel
+
+ @Test
+ fun onScrimClicked_hidesShade() =
+ testScope.runTest {
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ sceneInteractor.showOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = "test",
+ )
+ assertThat(currentOverlays).contains(Overlays.NotificationsShade)
+
+ underTest.onScrimClicked()
+
+ assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt
similarity index 81%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt
index 0505e19..46b02e92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt
@@ -37,8 +37,7 @@
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
-import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneActionsViewModel
+import com.android.systemui.shade.ui.viewmodel.notificationsShadeUserActionsViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,14 +52,14 @@
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@EnableSceneContainer
-class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() {
+class NotificationsShadeUserActionsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
- private val underTest by lazy { kosmos.notificationsShadeSceneActionsViewModel }
+ private val underTest by lazy { kosmos.notificationsShadeUserActionsViewModel }
@Test
fun upTransitionSceneKey_deviceLocked_lockscreen() =
@@ -104,36 +103,6 @@
}
@Test
- fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
- testScope.runTest {
- kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
- val actions by collectLastValue(underTest.actions)
- lockDevice()
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
- .isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
- testScope.runTest {
- kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
- val actions by collectLastValue(underTest.actions)
- lockDevice()
- unlockDevice()
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
- }
-
- @Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val actions by collectLastValue(underTest.actions)
@@ -153,11 +122,13 @@
@Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
+ val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
val actions by collectLastValue(underTest.actions)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.None
)
+ assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
sceneInteractor // force the lazy; this will kick off StateFlows
runCurrent()
sceneInteractor.changeScene(Scenes.Gone, "reason")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
index 1e5599b..93ba265 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
@@ -19,7 +19,6 @@
import android.content.ComponentName
import android.content.packageManager
import android.content.pm.PackageManager
-import android.content.pm.ServiceInfo
import android.content.pm.UserInfo
import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -35,6 +34,7 @@
import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository
import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
@@ -100,6 +100,7 @@
Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)),
Text.Loaded(tileService1),
Text.Loaded(appName1),
+ TileCategory.PROVIDED_BY_APP,
)
val expectedData2 =
EditTileData(
@@ -107,6 +108,7 @@
Icon.Loaded(drawable2, ContentDescription.Loaded(tileService2)),
Text.Loaded(tileService2),
Text.Loaded(appName2),
+ TileCategory.PROVIDED_BY_APP,
)
assertThat(editTileDataList).containsExactly(expectedData1, expectedData2)
@@ -144,6 +146,7 @@
Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)),
Text.Loaded(tileService1),
Text.Loaded(appName1),
+ TileCategory.PROVIDED_BY_APP,
)
val editTileDataList = underTest.getCustomTileData()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
index deefbf5..053a59a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
@@ -132,6 +133,7 @@
icon = Icon.Loaded(icon, ContentDescription.Loaded(tileName)),
label = Text.Loaded(tileName),
appName = Text.Loaded(appName),
+ category = TileCategory.PROVIDED_BY_APP,
)
assertThat(editTiles.customTiles).hasSize(1)
@@ -181,7 +183,8 @@
tileSpec = this,
icon = Icon.Resource(android.R.drawable.star_on, ContentDescription.Loaded(spec)),
label = Text.Loaded(spec),
- appName = null
+ appName = null,
+ category = TileCategory.UNKNOWN,
)
}
@@ -192,6 +195,7 @@
Icon.Resource(uiConfig.iconRes, ContentDescription.Resource(uiConfig.labelRes)),
label = Text.Resource(uiConfig.labelRes),
appName = null,
+ category = category,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index 7f01fad..484a8ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -16,11 +16,11 @@
package com.android.systemui.qs.panels.ui.compose
+import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.Text
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.model.GridCell
@@ -28,6 +28,7 @@
import com.android.systemui.qs.panels.ui.model.TileGridCell
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -141,10 +142,11 @@
EditTileViewModel(
tileSpec = TileSpec.create(tileSpec),
icon = Icon.Resource(0, null),
- label = Text.Loaded("unused"),
+ label = AnnotatedString("unused"),
appName = null,
isCurrent = true,
availableEditActions = emptySet(),
+ category = TileCategory.UNKNOWN,
),
width,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
index 601779f..583db72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.ui.compose.toAnnotatedString
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
@@ -42,6 +43,7 @@
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
@@ -190,7 +192,7 @@
.forEach {
val data = getEditTileData(it.tileSpec)
- assertThat(it.label).isEqualTo(data.label)
+ assertThat(it.label).isEqualTo(data.label.toAnnotatedString(context))
assertThat(it.icon).isEqualTo(data.icon)
assertThat(it.appName).isNull()
}
@@ -224,15 +226,19 @@
// service1
val model1 = tiles!!.first { it.tileSpec == TileSpec.create(component1) }
- assertThat(model1.label).isEqualTo(Text.Loaded(tileService1))
- assertThat(model1.appName).isEqualTo(Text.Loaded(appName1))
+ assertThat(model1.label)
+ .isEqualTo(Text.Loaded(tileService1).toAnnotatedString(context))
+ assertThat(model1.appName)
+ .isEqualTo(Text.Loaded(appName1).toAnnotatedString(context))
assertThat(model1.icon)
.isEqualTo(Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)))
// service2
val model2 = tiles!!.first { it.tileSpec == TileSpec.create(component2) }
- assertThat(model2.label).isEqualTo(Text.Loaded(tileService2))
- assertThat(model2.appName).isEqualTo(Text.Loaded(appName2))
+ assertThat(model2.label)
+ .isEqualTo(Text.Loaded(tileService2).toAnnotatedString(context))
+ assertThat(model2.appName)
+ .isEqualTo(Text.Loaded(appName2).toAnnotatedString(context))
assertThat(model2.icon)
.isEqualTo(Icon.Loaded(drawable2, ContentDescription.Loaded(tileService2)))
}
@@ -559,7 +565,8 @@
tileSpec = this,
icon = Icon.Resource(R.drawable.star_on, ContentDescription.Loaded(spec)),
label = Text.Loaded(spec),
- appName = null
+ appName = null,
+ category = TileCategory.UNKNOWN,
)
}
@@ -570,6 +577,7 @@
Icon.Resource(uiConfig.iconRes, ContentDescription.Resource(uiConfig.labelRes)),
label = Text.Resource(uiConfig.labelRes),
appName = null,
+ category = category,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
index d153e9d..5619022 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
@@ -21,14 +21,15 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,10 +39,11 @@
@RunWith(AndroidJUnit4::class)
class AutoAddableSettingTest : SysuiTestCase() {
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val secureSettings = kosmos.fakeSettings
- private val secureSettings = FakeSettings()
private val underTest =
AutoAddableSetting(
secureSettings,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt
new file mode 100644
index 0000000..7f90e3b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.qs.shared.model
+
+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 org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GroupAndSortCategoryAndNameTest : SysuiTestCase() {
+
+ private val elements =
+ listOf(
+ CategoryAndName(TileCategory.DISPLAY, "B"),
+ CategoryAndName(TileCategory.PRIVACY, "A"),
+ CategoryAndName(TileCategory.DISPLAY, "C"),
+ CategoryAndName(TileCategory.UTILITIES, "B"),
+ CategoryAndName(TileCategory.CONNECTIVITY, "A"),
+ CategoryAndName(TileCategory.PROVIDED_BY_APP, "B"),
+ CategoryAndName(TileCategory.CONNECTIVITY, "C"),
+ CategoryAndName(TileCategory.ACCESSIBILITY, "A")
+ )
+
+ @Test
+ fun allElementsInResult() {
+ val grouped = groupAndSort(elements)
+ val allValues = grouped.values.reduce { acc, el -> acc + el }
+ assertThat(allValues).containsExactlyElementsIn(elements)
+ }
+
+ @Test
+ fun groupedByCategory() {
+ val grouped = groupAndSort(elements)
+ grouped.forEach { tileCategory, categoryAndNames ->
+ categoryAndNames.forEach { element ->
+ assertThat(element.category).isEqualTo(tileCategory)
+ }
+ }
+ }
+
+ @Test
+ fun sortedAlphabeticallyInEachCategory() {
+ val grouped = groupAndSort(elements)
+ grouped.values.forEach { elements ->
+ assertThat(elements.map(CategoryAndName::name)).isInOrder()
+ }
+ }
+
+ @Test
+ fun categoriesSortedInNaturalOrder() {
+ val grouped = groupAndSort(elements)
+ assertThat(grouped.keys).isInOrder()
+ }
+
+ @Test
+ fun missingCategoriesAreNotInResult() {
+ val grouped = groupAndSort(elements.filterNot { it.category == TileCategory.CONNECTIVITY })
+ assertThat(grouped.keys).doesNotContain(TileCategory.CONNECTIVITY)
+ }
+
+ companion object {
+ private fun CategoryAndName(category: TileCategory, name: String): CategoryAndName {
+ return object : CategoryAndName {
+ override val category = category
+ override val name = name
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
index 79fcc92..d27e810 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
@@ -29,8 +29,9 @@
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -40,8 +41,9 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class AirplaneModeTileUserActionInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
- private val mobileConnectionsRepository = FakeMobileConnectionsRepository()
+ private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
private val connectivityRepository = FakeConnectivityRepository()
private val airplaneModeRepository = FakeAirplaneModeRepository()
private val inputHandler = FakeQSTileIntentUserInputHandler()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 1ea8abc9..5a45060 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -33,7 +33,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.res.R
@@ -86,7 +86,7 @@
private val wifiInteractor =
WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
- private val tableLogBuffer: TableLogBuffer = mock()
+ private val tableLogBuffer = logcatTableLogBuffer(kosmos, "InternetTileDataInteractorTest")
private val carrierConfigTracker: CarrierConfigTracker = mock()
private val mobileConnectionsRepository =
@@ -184,8 +184,7 @@
)
val networkModel =
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
)
@@ -220,8 +219,7 @@
)
val networkModel =
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
@@ -400,7 +398,7 @@
collectLastValue(
underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
)
- val networkModel = WifiNetworkModel.Inactive
+ val networkModel = WifiNetworkModel.Inactive()
connectivityRepository.setWifiConnected(validated = false)
wifiRepository.setIsWifiDefault(true)
@@ -418,7 +416,7 @@
underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
)
- val networkModel = WifiNetworkModel.Inactive
+ val networkModel = WifiNetworkModel.Inactive()
connectivityRepository.setWifiConnected(validated = false)
wifiRepository.setIsWifiDefault(true)
@@ -545,8 +543,7 @@
private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
val networkModel =
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
hotspotDeviceType = hotspot,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
index 2444229..fa6d8bf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.kosmos.testCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
@@ -43,7 +44,8 @@
QSTileConfig(
TileSpec.create(RecordIssueModule.TILE_SPEC),
uiConfig,
- kosmos.qsEventLogger.getNewInstanceId()
+ kosmos.qsEventLogger.getNewInstanceId(),
+ TileCategory.UTILITIES,
)
private val resources = kosmos.mainResources
private val theme = resources.newTheme()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
index 4e58069..5bd3645 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.recordissue.RecordIssueDialogDelegate
+import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
@@ -40,12 +41,16 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class IssueRecordingUserActionInteractorTest : SysuiTestCase() {
+ @Mock private lateinit var recordingController: RecordingController
+
val user = UserHandle(1)
val kosmos = Kosmos().also { it.testCase = this }
@@ -56,6 +61,7 @@
@Before
fun setup() {
+ MockitoAnnotations.initMocks(this)
hasCreatedDialogDelegate = false
with(kosmos) {
val factory =
@@ -84,7 +90,8 @@
dialogTransitionAnimator,
panelInteractor,
userTracker,
- factory
+ factory,
+ recordingController,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index 5925819..de3dc57 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -24,9 +24,12 @@
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.notification.modes.ZenIconLoader
+import com.android.internal.R
+import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.systemui.SysuiTestCase
+import com.android.systemui.SysuiTestableContext
import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -36,7 +39,6 @@
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toCollection
@@ -60,10 +62,15 @@
@Before
fun setUp() {
context.orCreateTestableResources.apply {
- addOverride(com.android.internal.R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE)
- addOverride(com.android.internal.R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE)
+ addOverride(MODES_DRAWABLE_ID, MODES_DRAWABLE)
+ addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE)
}
- ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService()))
+
+ val customPackageContext = SysuiTestableContext(context)
+ context.prepareCreatePackageContext(CUSTOM_PACKAGE, customPackageContext)
+ customPackageContext.orCreateTestableResources.apply {
+ addOverride(CUSTOM_DRAWABLE_ID, CUSTOM_DRAWABLE)
+ }
}
@EnableFlags(Flags.FLAG_MODES_UI)
@@ -128,60 +135,136 @@
@Test
@EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
- fun changesIconWhenActiveModesChange() =
+ fun tileData_iconsFlagEnabled_changesIconWhenActiveModesChange() =
testScope.runTest {
- val dataList: List<ModesTileModel> by
- collectValues(
+ val tileData by
+ collectLastValue(
underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
)
- runCurrent()
- assertThat(dataList.map { it.icon }).containsExactly(null).inOrder()
- // Add an inactive mode: state hasn't changed, so this shouldn't cause another emission
+ // Tile starts with the generic Modes icon.
+ runCurrent()
+ assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+ assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+
+ // Add an inactive mode -> Still modes icon
zenModeRepository.addMode(id = "Mode", active = false)
runCurrent()
- assertThat(dataList.map { it.icon }).containsExactly(null).inOrder()
+ assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+ assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
- // Add an active mode: icon should be the mode icon
+ // Add an active mode with a default icon: icon should be the mode icon, and the
+ // iconResId is also populated, because we know it's a system icon.
zenModeRepository.addMode(
- id = "Bedtime",
+ id = "Bedtime with default icon",
type = AutomaticZenRule.TYPE_BEDTIME,
active = true
)
runCurrent()
- assertThat(dataList.map { it.icon }).containsExactly(null, BEDTIME_ICON).inOrder()
+ assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
+ assertThat(tileData?.iconResId).isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
- // Add another, less-prioritized mode: icon should remain the first mode icon
+ // Add another, less-prioritized mode that has a *custom* icon: for now, icon should
+ // remain the first mode icon
zenModeRepository.addMode(
- id = "Driving",
- type = AutomaticZenRule.TYPE_DRIVING,
- active = true
+ TestModeBuilder()
+ .setId("Driving with custom icon")
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setPackage(CUSTOM_PACKAGE)
+ .setIconResId(CUSTOM_DRAWABLE_ID)
+ .setActive(true)
+ .build()
)
runCurrent()
- assertThat(dataList.map { it.icon })
- .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON)
- .inOrder()
+ assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
+ assertThat(tileData?.iconResId).isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
- // Deactivate more important mode: icon should be the less important, still active mode.
- zenModeRepository.deactivateMode("Bedtime")
+ // Deactivate more important mode: icon should be the less important, still active mode
+ // And because it's a package-provided icon, iconResId is not populated.
+ zenModeRepository.deactivateMode("Bedtime with default icon")
runCurrent()
- assertThat(dataList.map { it.icon })
- .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON, DRIVING_ICON)
- .inOrder()
+ assertThat(tileData?.icon).isEqualTo(CUSTOM_ICON)
+ assertThat(tileData?.iconResId).isNull()
- // Deactivate remaining mode: no icon
- zenModeRepository.deactivateMode("Driving")
+ // Deactivate remaining mode: back to the default modes icon
+ zenModeRepository.deactivateMode("Driving with custom icon")
runCurrent()
- assertThat(dataList.map { it.icon })
- .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON, DRIVING_ICON, null)
- .inOrder()
+ assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+ assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
}
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI)
+ @DisableFlags(Flags.FLAG_MODES_UI_ICONS)
+ fun tileData_iconsFlagDisabled_hasPriorityModesIcon() =
+ testScope.runTest {
+ val tileData by
+ collectLastValue(
+ underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+
+ runCurrent()
+ assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+ assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+
+ // Activate a Mode -> Icon doesn't change.
+ zenModeRepository.addMode(id = "Mode", active = true)
+ runCurrent()
+ assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+ assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+
+ zenModeRepository.deactivateMode(id = "Mode")
+ runCurrent()
+ assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+ assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ }
+
+ @EnableFlags(Flags.FLAG_MODES_UI)
+ @Test
+ fun getCurrentTileModel_returnsActiveModes() = runTest {
+ var tileData = underTest.getCurrentTileModel()
+ assertThat(tileData.isActivated).isFalse()
+ assertThat(tileData.activeModes).isEmpty()
+
+ // Add active mode
+ zenModeRepository.addMode(id = "One", active = true)
+ tileData = underTest.getCurrentTileModel()
+ assertThat(tileData.isActivated).isTrue()
+ assertThat(tileData.activeModes).containsExactly("Mode One")
+
+ // Add an inactive mode: state hasn't changed
+ zenModeRepository.addMode(id = "Two", active = false)
+ tileData = underTest.getCurrentTileModel()
+ assertThat(tileData.isActivated).isTrue()
+ assertThat(tileData.activeModes).containsExactly("Mode One")
+
+ // Add another active mode
+ zenModeRepository.addMode(id = "Three", active = true)
+ tileData = underTest.getCurrentTileModel()
+ assertThat(tileData.isActivated).isTrue()
+ assertThat(tileData.activeModes).containsExactly("Mode One", "Mode Three").inOrder()
+
+ // Remove a mode and deactivate the other
+ zenModeRepository.removeMode("One")
+ zenModeRepository.deactivateMode("Three")
+ tileData = underTest.getCurrentTileModel()
+ assertThat(tileData.isActivated).isFalse()
+ assertThat(tileData.activeModes).isEmpty()
+ }
+
private companion object {
val TEST_USER = UserHandle.of(1)!!
+ const val CUSTOM_PACKAGE = "com.some.mode.owner.package"
+
+ val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
+ const val CUSTOM_DRAWABLE_ID = 12345
+
+ val MODES_DRAWABLE = TestStubDrawable("modes_icon")
val BEDTIME_DRAWABLE = TestStubDrawable("bedtime")
- val DRIVING_DRAWABLE = TestStubDrawable("driving")
+ val CUSTOM_DRAWABLE = TestStubDrawable("custom")
+
+ val MODES_ICON = MODES_DRAWABLE.asIcon()
val BEDTIME_ICON = BEDTIME_DRAWABLE.asIcon()
- val DRIVING_ICON = DRIVING_DRAWABLE.asIcon()
+ val CUSTOM_ICON = CUSTOM_DRAWABLE.asIcon()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index 4b75649..cd58127 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -16,12 +16,14 @@
package com.android.systemui.qs.tiles.impl.modes.domain.interactor
+import android.graphics.drawable.TestStubDrawable
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
@@ -54,10 +56,7 @@
fun handleClick_active() = runTest {
val expandable = mock<Expandable>()
underTest.handleInput(
- QSTileInputTestKtx.click(
- data = ModesTileModel(true, listOf("DND")),
- expandable = expandable
- )
+ QSTileInputTestKtx.click(data = modelOf(true, listOf("DND")), expandable = expandable)
)
verify(mockDialogDelegate).showDialog(eq(expandable))
@@ -67,10 +66,7 @@
fun handleClick_inactive() = runTest {
val expandable = mock<Expandable>()
underTest.handleInput(
- QSTileInputTestKtx.click(
- data = ModesTileModel(false, emptyList()),
- expandable = expandable
- )
+ QSTileInputTestKtx.click(data = modelOf(false, emptyList()), expandable = expandable)
)
verify(mockDialogDelegate).showDialog(eq(expandable))
@@ -78,7 +74,7 @@
@Test
fun handleLongClick_active() = runTest {
- underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(true, listOf("DND"))))
+ underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(true, listOf("DND"))))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
@@ -87,10 +83,14 @@
@Test
fun handleLongClick_inactive() = runTest {
- underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(false, emptyList())))
+ underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(false, emptyList())))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
}
}
+
+ private fun modelOf(isActivated: Boolean, activeModeNames: List<String>): ModesTileModel {
+ return ModesTileModel(isActivated, activeModeNames, TestStubDrawable("icon").asIcon(), 123)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
index a41f15d..c3d45db 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
@@ -22,7 +22,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
import com.android.systemui.qs.tiles.viewmodel.QSTileState
@@ -58,47 +58,69 @@
@Test
fun inactiveState() {
- val model = ModesTileModel(isActivated = false, activeModes = emptyList())
+ val icon = TestStubDrawable("res123").asIcon()
+ val model =
+ ModesTileModel(
+ isActivated = false,
+ activeModes = emptyList(),
+ icon = icon,
+ )
val state = underTest.map(config, model)
assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE)
- assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_off)
+ assertThat(state.icon()).isEqualTo(icon)
assertThat(state.secondaryLabel).isEqualTo("No active modes")
}
@Test
fun activeState_oneMode() {
- val model = ModesTileModel(isActivated = true, activeModes = listOf("DND"))
+ val icon = TestStubDrawable("res123").asIcon()
+ val model =
+ ModesTileModel(
+ isActivated = true,
+ activeModes = listOf("DND"),
+ icon = icon,
+ )
val state = underTest.map(config, model)
assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
- assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on)
+ assertThat(state.icon()).isEqualTo(icon)
assertThat(state.secondaryLabel).isEqualTo("DND is active")
}
@Test
fun activeState_multipleModes() {
+ val icon = TestStubDrawable("res123").asIcon()
val model =
- ModesTileModel(isActivated = true, activeModes = listOf("Mode 1", "Mode 2", "Mode 3"))
+ ModesTileModel(
+ isActivated = true,
+ activeModes = listOf("Mode 1", "Mode 2", "Mode 3"),
+ icon = icon,
+ )
val state = underTest.map(config, model)
assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
- assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on)
+ assertThat(state.icon()).isEqualTo(icon)
assertThat(state.secondaryLabel).isEqualTo("3 modes are active")
}
@Test
- @EnableFlags(Flags.FLAG_MODES_UI_ICONS)
- fun activeState_withIcon() {
- val icon = Icon.Resource(1234, contentDescription = null)
- val model = ModesTileModel(isActivated = true, activeModes = listOf("DND"), icon = icon)
+ fun state_modelHasIconResId_includesIconResId() {
+ val icon = TestStubDrawable("res123").asIcon()
+ val model =
+ ModesTileModel(
+ isActivated = false,
+ activeModes = emptyList(),
+ icon = icon,
+ iconResId = 123
+ )
val state = underTest.map(config, model)
- assertThat(state.iconRes).isNull()
assertThat(state.icon()).isEqualTo(icon)
+ assertThat(state.iconRes).isEqualTo(123)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
index 3133312..b54fd86 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
@@ -19,12 +19,15 @@
import android.platform.test.annotations.EnabledOnRavenwood
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.server.display.feature.flags.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
import com.android.systemui.accessibility.reduceBrightColorsController
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
@@ -33,8 +36,14 @@
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.verify
@SmallTest
@EnabledOnRavenwood
@@ -45,21 +54,34 @@
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val controller = kosmos.reduceBrightColorsController
- private val underTest =
- ReduceBrightColorsTileUserActionInteractor(
- context.resources,
- inputHandler,
- controller,
- )
+ @Mock private lateinit var mExtraDimDialogManager: ExtraDimDialogManager
- private val underTestEvenDimmerEnabled =
- ReduceBrightColorsTileUserActionInteractor(
- context.orCreateTestableResources
- .apply { addOverride(R.bool.config_evenDimmerEnabled, true) }
- .resources,
- inputHandler,
- controller,
- )
+ @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ private lateinit var underTest: ReduceBrightColorsTileUserActionInteractor
+ private lateinit var underTestEvenDimmerEnabled: ReduceBrightColorsTileUserActionInteractor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ ReduceBrightColorsTileUserActionInteractor(
+ context.resources,
+ inputHandler,
+ controller,
+ mExtraDimDialogManager,
+ )
+
+ underTestEvenDimmerEnabled =
+ ReduceBrightColorsTileUserActionInteractor(
+ context.orCreateTestableResources
+ .apply { addOverride(R.bool.config_evenDimmerEnabled, true) }
+ .resources,
+ inputHandler,
+ controller,
+ mExtraDimDialogManager,
+ )
+ }
@Test
@RequiresFlagsDisabled(Flags.FLAG_EVEN_DIMMER)
@@ -142,9 +164,7 @@
QSTileInputTestKtx.longClick(ReduceBrightColorsTileModel(enabled))
)
- QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
- assertThat(it.intent.action).isEqualTo(Settings.ACTION_DISPLAY_SETTINGS)
- }
+ verify(mExtraDimDialogManager).dismissKeyguardIfNeededAndShowDialog(anyOrNull())
}
@Test
@@ -155,9 +175,6 @@
underTestEvenDimmerEnabled.handleInput(
QSTileInputTestKtx.longClick(ReduceBrightColorsTileModel(enabled))
)
-
- QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
- assertThat(it.intent.action).isEqualTo(Settings.ACTION_DISPLAY_SETTINGS)
- }
+ verify(mExtraDimDialogManager).dismissKeyguardIfNeededAndShowDialog(anyOrNull())
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
index e2149d9..424afe1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.lifecycle.LifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -26,20 +27,26 @@
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -51,6 +58,7 @@
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@EnableSceneContainer
+@DisableFlags(com.android.systemui.Flags.FLAG_DUAL_SHADE)
class QuickSettingsSceneContentViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -64,6 +72,8 @@
private val footerActionsController = mock<FooterActionsController>()
private val sceneContainerStartable = kosmos.sceneContainerStartable
+ private val sceneInteractor by lazy { kosmos.sceneInteractor }
+ private val shadeInteractor by lazy { kosmos.shadeInteractor }
private lateinit var underTest: QuickSettingsSceneContentViewModel
@@ -80,7 +90,10 @@
footerActionsViewModelFactory = footerActionsViewModelFactory,
footerActionsController = footerActionsController,
mediaCarouselInteractor = kosmos.mediaCarouselInteractor,
+ shadeInteractor = shadeInteractor,
+ sceneInteractor = sceneInteractor,
)
+ underTest.activateIn(testScope)
}
@Test
@@ -122,4 +135,16 @@
assertThat(isMediaVisible).isTrue()
}
+
+ @Test
+ fun shadeModeChange_switchToShadeScene() =
+ testScope.runTest {
+ val scene by collectLastValue(sceneInteractor.currentScene)
+
+ // switch to split shade
+ kosmos.shadeRepository.setShadeLayoutWide(true)
+ runCurrent()
+
+ assertThat(scene).isEqualTo(Scenes.Shade)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
new file mode 100644
index 0000000..762941d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val underTest = kosmos.quickSettingsShadeOverlayActionsViewModel
+
+ @Test
+ fun upTransitionSceneKey_hidesShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+ underTest.activateIn(this)
+
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+ .isEqualTo(Overlays.QuickSettingsShade)
+ assertThat(actions?.get(Swipe.Down)).isNull()
+ }
+
+ @Test
+ fun back_hidesShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+ underTest.activateIn(this)
+
+ assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+ .isEqualTo(Overlays.QuickSettingsShade)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
new file mode 100644
index 0000000..abd1e2c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val sceneInteractor = kosmos.sceneInteractor
+
+ private val underTest = kosmos.quickSettingsShadeOverlayContentViewModel
+
+ @Test
+ fun onScrimClicked_hidesShade() =
+ testScope.runTest {
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ sceneInteractor.showOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = "test",
+ )
+ assertThat(currentOverlays).contains(Overlays.QuickSettingsShade)
+
+ underTest.onScrimClicked()
+
+ assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt
similarity index 82%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt
index db58c85..32772d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt
@@ -39,7 +39,6 @@
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -54,14 +53,14 @@
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@EnableSceneContainer
-class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() {
+class QuickSettingsShadeUserActionsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneInteractor = kosmos.sceneInteractor
private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
- private val underTest by lazy { kosmos.quickSettingsShadeSceneActionsViewModel }
+ private val underTest by lazy { kosmos.quickSettingsShadeUserActionsViewModel }
@Test
fun upTransitionSceneKey_deviceLocked_lockscreen() =
@@ -107,37 +106,6 @@
}
@Test
- fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
- testScope.runTest {
- kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- lockDevice()
-
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
- testScope.runTest {
- kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- lockDevice()
- unlockDevice()
-
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat(homeScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
underTest.activateIn(this)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
similarity index 98%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
index f26a9db5..6986cf8e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
@@ -57,7 +57,7 @@
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@EnableSceneContainer
-class QuickSettingsSceneActionsViewModelTest : SysuiTestCase() {
+class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -67,7 +67,7 @@
private val sceneBackInteractor = kosmos.sceneBackInteractor
private val sceneContainerStartable = kosmos.sceneContainerStartable
- private lateinit var underTest: QuickSettingsSceneActionsViewModel
+ private lateinit var underTest: QuickSettingsUserActionsViewModel
@Before
fun setUp() {
@@ -75,7 +75,7 @@
sceneContainerStartable.start()
underTest =
- QuickSettingsSceneActionsViewModel(
+ QuickSettingsUserActionsViewModel(
qsSceneAdapter = qsFlexiglassAdapter,
sceneBackInteractor = sceneBackInteractor,
)
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 f365afb..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.LockscreenSceneActionsViewModel
+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.ShadeSceneActionsViewModel
-import com.android.systemui.shade.ui.viewmodel.ShadeSceneContentViewModel
-import com.android.systemui.shade.ui.viewmodel.shadeSceneActionsViewModel
import com.android.systemui.shade.ui.viewmodel.shadeSceneContentViewModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.shade.ui.viewmodel.shadeUserActionsViewModel
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 lockscreenSceneActionsViewModel by lazy {
- LockscreenSceneActionsViewModel(
- deviceEntryInteractor = deviceEntryInteractor,
- communalInteractor = communalInteractor,
- shadeInteractor = kosmos.shadeInteractor,
- )
- }
-
- private lateinit var shadeSceneContentViewModel: ShadeSceneContentViewModel
- private lateinit var shadeSceneActionsViewModel: ShadeSceneActionsViewModel
-
- 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
- shadeSceneActionsViewModel = kosmos.shadeSceneActionsViewModel
-
- val startable = kosmos.sceneContainerStartable
- startable.start()
-
- lockscreenSceneActionsViewModel.activateIn(testScope)
- shadeSceneContentViewModel.activateIn(testScope)
- shadeSceneActionsViewModel.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(lockscreenSceneActionsViewModel.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(lockscreenSceneActionsViewModel.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(shadeSceneActionsViewModel.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(shadeSceneActionsViewModel.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(lockscreenSceneActionsViewModel.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(lockscreenSceneActionsViewModel.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(lockscreenSceneActionsViewModel.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(lockscreenSceneActionsViewModel.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/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index d3b51d1..d180460 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -19,7 +19,10 @@
package com.android.systemui.scene.domain.startable
import android.app.StatusBarManager
+import android.hardware.face.FaceManager
import android.os.PowerManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -27,9 +30,14 @@
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.uiEventLoggerFake
import com.android.internal.policy.IKeyguardDismissCallback
+import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
@@ -39,8 +47,15 @@
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
+import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
@@ -53,11 +68,15 @@
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.scenetransition.lockscreenSceneTransitionInteractor
+import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.data.repository.powerRepository
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
@@ -69,6 +88,7 @@
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
@@ -80,11 +100,13 @@
import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
+import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -92,6 +114,8 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -105,18 +129,22 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
+ private val deviceEntryHapticsInteractor by lazy { kosmos.deviceEntryHapticsInteractor }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
private val sysUiState = kosmos.sysUiState
private val falsingCollector = mock<FalsingCollector>().also { kosmos.falsingCollector = it }
+ private val vibratorHelper = mock<VibratorHelper>().also { kosmos.vibratorHelper = it }
private val fakeSceneDataSource = kosmos.fakeSceneDataSource
private val windowController = kosmos.notificationShadeWindowController
private val centralSurfaces = kosmos.centralSurfaces
private val powerInteractor = kosmos.powerInteractor
private val fakeTrustRepository = kosmos.fakeTrustRepository
private val uiEventLoggerFake = kosmos.uiEventLoggerFake
+ private val msdlPlayer = kosmos.fakeMSDLPlayer
+ private val authInteractionProperties = AuthInteractionProperties()
private lateinit var underTest: SceneContainerStartable
@@ -634,6 +662,382 @@
}
@Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playSuccessHaptics_onSuccessfulLockscreenAuth_udfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasUdfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper)
+ .vibrateAuthSuccess(
+ "SceneContainerStartable, $currentSceneKey device-entry::success"
+ )
+ verify(vibratorHelper, never()).vibrateAuthError(anyString())
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playSuccessMSDLHaptics_onSuccessfulLockscreenAuth_udfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasUdfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playSuccessHaptics_onSuccessfulLockscreenAuth_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps()
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper)
+ .vibrateAuthSuccess(
+ "SceneContainerStartable, $currentSceneKey device-entry::success"
+ )
+ verify(vibratorHelper, never()).vibrateAuthError(anyString())
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playSuccessMSDLHaptics_onSuccessfulLockscreenAuth_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps()
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playErrorHaptics_onFailedLockscreenAuth_udfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasUdfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper)
+ .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+ verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playMSDLErrorHaptics_onFailedLockscreenAuth_udfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasUdfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playErrorHaptics_onFailedLockscreenAuth_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper)
+ .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+ verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playMSDLErrorHaptics_onFailedLockscreenAuth_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsSuccessHaptics_whenPowerButtonDown_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps(isPowerButtonDown = true)
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper, never())
+ .vibrateAuthSuccess(
+ "SceneContainerStartable, $currentSceneKey device-entry::success"
+ )
+ verify(vibratorHelper, never()).vibrateAuthError(anyString())
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsMSDLSuccessHaptics_whenPowerButtonDown_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps(isPowerButtonDown = true)
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsSuccessHaptics_whenPowerButtonRecentlyPressed_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps(lastPowerPress = 50)
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper, never())
+ .vibrateAuthSuccess(
+ "SceneContainerStartable, $currentSceneKey device-entry::success"
+ )
+ verify(vibratorHelper, never()).vibrateAuthError(anyString())
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsMSDLSuccessHaptics_whenPowerButtonRecentlyPressed_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps(lastPowerPress = 50)
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsErrorHaptics_whenPowerButtonDown_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper, never())
+ .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+ verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsMSDLErrorHaptics_whenPowerButtonDown_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsFaceErrorHaptics_nonSfps_coEx() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasUdfps = true, hasFace = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFaceAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper, never())
+ .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+ verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsMSDLFaceErrorHaptics_nonSfps_coEx() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasUdfps = true, hasFace = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFaceAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
+
+ @Test
fun hydrateSystemUiState() =
testScope.runTest {
val transitionStateFlow = prepareState()
@@ -1192,41 +1596,6 @@
}
@Test
- fun hydrateWindowController_setBouncerShowing() =
- testScope.runTest {
- underTest.start()
- val notificationShadeWindowController = kosmos.notificationShadeWindowController
- val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen)
- val currentScene by collectLastValue(sceneInteractor.currentScene)
- assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- verify(notificationShadeWindowController, never()).setBouncerShowing(true)
- verify(notificationShadeWindowController, times(1)).setBouncerShowing(false)
-
- emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
- verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
- verify(notificationShadeWindowController, times(1)).setBouncerShowing(false)
-
- emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
- verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
- verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
-
- kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- assertThat(currentScene).isEqualTo(Scenes.Gone)
- verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
- verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
-
- emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
- verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
- verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
-
- emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
- verify(notificationShadeWindowController, times(2)).setBouncerShowing(true)
- verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
- }
-
- @Test
fun hydrateWindowController_setKeyguardOccluded() =
testScope.runTest {
underTest.start()
@@ -1876,4 +2245,92 @@
FakeHeadsUpRowRepository(key = key, elementKey = Any()).apply {
this.isPinned.value = isPinned
}
+
+ private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) {
+ kosmos.fingerprintPropertyRepository.setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = fingerprintSensorType,
+ sensorLocations = mapOf(),
+ )
+ kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ }
+
+ private fun setFaceEnrolled() {
+ kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ }
+
+ private fun TestScope.allowHapticsOnSfps(
+ isPowerButtonDown: Boolean = false,
+ lastPowerPress: Long = 10000
+ ) {
+ kosmos.fakeKeyEventRepository.setPowerButtonDown(isPowerButtonDown)
+
+ kosmos.powerRepository.updateWakefulness(
+ WakefulnessState.AWAKE,
+ WakeSleepReason.POWER_BUTTON,
+ WakeSleepReason.POWER_BUTTON,
+ powerButtonLaunchGestureTriggered = false,
+ )
+
+ advanceTimeBy(lastPowerPress)
+ runCurrent()
+ }
+
+ private fun unlockWithFingerprintAuth() {
+ kosmos.fakeKeyguardRepository.setBiometricUnlockSource(
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ )
+ kosmos.fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockMode.UNLOCK_COLLAPSING)
+ }
+
+ private fun TestScope.setupBiometricAuth(
+ hasSfps: Boolean = false,
+ hasUdfps: Boolean = false,
+ hasFace: Boolean = false
+ ) {
+ if (hasSfps) {
+ setFingerprintSensorType(FingerprintSensorType.POWER_BUTTON)
+ }
+
+ if (hasUdfps) {
+ setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+ }
+
+ if (hasFace) {
+ setFaceEnrolled()
+ }
+
+ prepareState(
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ )
+ }
+
+ private fun updateFingerprintAuthStatus(isSuccess: Boolean) {
+ if (isSuccess) {
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ } else {
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ FailFingerprintAuthenticationStatus
+ )
+ }
+ }
+
+ private fun updateFaceAuthStatus(isSuccess: Boolean) {
+ if (isSuccess) {
+ kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(
+ SuccessFaceAuthenticationStatus(
+ successResult = Mockito.mock(FaceManager.AuthenticationResult::class.java)
+ )
+ )
+ } else {
+ kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(
+ FailedFaceAuthenticationStatus()
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
similarity index 68%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
index b526275..e6a24e3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -29,6 +31,7 @@
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -43,40 +46,50 @@
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@EnableSceneContainer
-class GoneSceneActionsViewModelTest : SysuiTestCase() {
+class GoneUserActionsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val shadeRepository by lazy { kosmos.shadeRepository }
- private lateinit var underTest: GoneSceneActionsViewModel
+ private lateinit var underTest: GoneUserActionsViewModel
@Before
fun setUp() {
- underTest =
- GoneSceneActionsViewModel(
- shadeInteractor = kosmos.shadeInteractor,
- )
+ underTest = GoneUserActionsViewModel(shadeInteractor = kosmos.shadeInteractor)
underTest.activateIn(testScope)
}
@Test
+ @DisableFlags(DualShade.FLAG_NAME)
fun downTransitionKey_splitShadeEnabled_isGoneToSplitShade() =
testScope.runTest {
- val destinationScenes by collectLastValue(underTest.actions)
+ val userActions by collectLastValue(underTest.actions)
shadeRepository.setShadeLayoutWide(true)
runCurrent()
- assertThat(destinationScenes?.get(Swipe(SwipeDirection.Down))?.transitionKey)
+ assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey)
.isEqualTo(ToSplitShade)
}
@Test
+ @DisableFlags(DualShade.FLAG_NAME)
fun downTransitionKey_splitShadeDisabled_isNull() =
testScope.runTest {
- val destinationScenes by collectLastValue(underTest.actions)
+ val userActions by collectLastValue(underTest.actions)
shadeRepository.setShadeLayoutWide(false)
runCurrent()
- assertThat(destinationScenes?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
+ assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun downTransitionKey_dualShadeEnabled_isNull() =
+ testScope.runTest {
+ val userActions by collectLastValue(underTest.actions)
+ shadeRepository.setShadeLayoutWide(true)
+ runCurrent()
+
+ assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
}
}
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..a0bb017 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
@@ -18,9 +18,12 @@
package com.android.systemui.scene.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.DefaultEdgeDetector
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.classifier.fakeFalsingManager
@@ -31,10 +34,16 @@
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.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -58,6 +67,7 @@
private val testScope by lazy { kosmos.testScope }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
+ private val fakeShadeRepository by lazy { kosmos.fakeShadeRepository }
private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
private val falsingManager by lazy { kosmos.fakeFalsingManager }
@@ -73,6 +83,8 @@
sceneInteractor = sceneInteractor,
falsingInteractor = kosmos.falsingInteractor,
powerInteractor = kosmos.powerInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
+ splitEdgeDetector = kosmos.splitEdgeDetector,
logger = kosmos.sceneLogger,
motionEventHandlerReceiver = { motionEventHandler ->
[email protected] = motionEventHandler
@@ -243,4 +255,90 @@
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)
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun edgeDetector_singleShade_usesDefaultEdgeDetector() =
+ testScope.runTest {
+ val shadeMode by collectLastValue(kosmos.shadeInteractor.shadeMode)
+ fakeShadeRepository.setShadeLayoutWide(false)
+ assertThat(shadeMode).isEqualTo(ShadeMode.Single)
+
+ assertThat(underTest.edgeDetector).isEqualTo(DefaultEdgeDetector)
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun edgeDetector_splitShade_usesDefaultEdgeDetector() =
+ testScope.runTest {
+ val shadeMode by collectLastValue(kosmos.shadeInteractor.shadeMode)
+ fakeShadeRepository.setShadeLayoutWide(true)
+ assertThat(shadeMode).isEqualTo(ShadeMode.Split)
+
+ assertThat(underTest.edgeDetector).isEqualTo(DefaultEdgeDetector)
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun edgeDetector_dualShade_narrowScreen_usesSplitEdgeDetector() =
+ testScope.runTest {
+ val shadeMode by collectLastValue(kosmos.shadeInteractor.shadeMode)
+ fakeShadeRepository.setShadeLayoutWide(false)
+
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+ assertThat(underTest.edgeDetector).isEqualTo(kosmos.splitEdgeDetector)
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun edgeDetector_dualShade_wideScreen_usesSplitEdgeDetector() =
+ testScope.runTest {
+ val shadeMode by collectLastValue(kosmos.shadeInteractor.shadeMode)
+ fakeShadeRepository.setShadeLayoutWide(true)
+
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+ assertThat(underTest.edgeDetector).isEqualTo(kosmos.splitEdgeDetector)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorTest.kt
new file mode 100644
index 0000000..3d76d28
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorTest.kt
@@ -0,0 +1,274 @@
+/*
+ * 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.scene.ui.viewmodel
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.End
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.Bottom
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.Left
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.Right
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.TopLeft
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.TopRight
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Start
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.TopEnd
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.TopStart
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SplitEdgeDetectorTest : SysuiTestCase() {
+
+ private val edgeSize = 40
+ private val screenWidth = 800
+ private val screenHeight = 600
+
+ private var edgeSplitFraction = 0.7f
+
+ private val underTest =
+ SplitEdgeDetector(
+ topEdgeSplitFraction = { edgeSplitFraction },
+ edgeSize = edgeSize.dp,
+ )
+
+ @Test
+ fun source_noEdge_detectsNothing() {
+ val detectedEdge =
+ swipeVerticallyFrom(
+ x = screenWidth / 2,
+ y = screenHeight / 2,
+ )
+ assertThat(detectedEdge).isNull()
+ }
+
+ @Test
+ fun source_swipeVerticallyOnTopLeft_detectsTopLeft() {
+ val detectedEdge =
+ swipeVerticallyFrom(
+ x = 1,
+ y = edgeSize - 1,
+ )
+ assertThat(detectedEdge).isEqualTo(TopLeft)
+ }
+
+ @Test
+ fun source_swipeHorizontallyOnTopLeft_detectsLeft() {
+ val detectedEdge =
+ swipeHorizontallyFrom(
+ x = 1,
+ y = edgeSize - 1,
+ )
+ assertThat(detectedEdge).isEqualTo(Left)
+ }
+
+ @Test
+ fun source_swipeVerticallyOnTopRight_detectsTopRight() {
+ val detectedEdge =
+ swipeVerticallyFrom(
+ x = screenWidth - 1,
+ y = edgeSize - 1,
+ )
+ assertThat(detectedEdge).isEqualTo(TopRight)
+ }
+
+ @Test
+ fun source_swipeHorizontallyOnTopRight_detectsRight() {
+ val detectedEdge =
+ swipeHorizontallyFrom(
+ x = screenWidth - 1,
+ y = edgeSize - 1,
+ )
+ assertThat(detectedEdge).isEqualTo(Right)
+ }
+
+ @Test
+ fun source_swipeVerticallyToLeftOfSplit_detectsTopLeft() {
+ val detectedEdge =
+ swipeVerticallyFrom(
+ x = (screenWidth * edgeSplitFraction).toInt() - 1,
+ y = edgeSize - 1,
+ )
+ assertThat(detectedEdge).isEqualTo(TopLeft)
+ }
+
+ @Test
+ fun source_swipeVerticallyToRightOfSplit_detectsTopRight() {
+ val detectedEdge =
+ swipeVerticallyFrom(
+ x = (screenWidth * edgeSplitFraction).toInt() + 1,
+ y = edgeSize - 1,
+ )
+ assertThat(detectedEdge).isEqualTo(TopRight)
+ }
+
+ @Test
+ fun source_edgeSplitFractionUpdatesDynamically() {
+ val middleX = (screenWidth * 0.5f).toInt()
+ val topY = 0
+
+ // Split closer to the right; middle of screen is considered "left".
+ edgeSplitFraction = 0.6f
+ assertThat(swipeVerticallyFrom(x = middleX, y = topY)).isEqualTo(TopLeft)
+
+ // Split closer to the left; middle of screen is considered "right".
+ edgeSplitFraction = 0.4f
+ assertThat(swipeVerticallyFrom(x = middleX, y = topY)).isEqualTo(TopRight)
+
+ // Illegal fraction.
+ edgeSplitFraction = 1.2f
+ assertFailsWith<IllegalArgumentException> { swipeVerticallyFrom(x = middleX, y = topY) }
+
+ // Illegal fraction.
+ edgeSplitFraction = -0.3f
+ assertFailsWith<IllegalArgumentException> { swipeVerticallyFrom(x = middleX, y = topY) }
+ }
+
+ @Test
+ fun source_swipeVerticallyOnBottom_detectsBottom() {
+ val detectedEdge =
+ swipeVerticallyFrom(
+ x = screenWidth / 3,
+ y = screenHeight - (edgeSize / 2),
+ )
+ assertThat(detectedEdge).isEqualTo(Bottom)
+ }
+
+ @Test
+ fun source_swipeHorizontallyOnBottom_detectsNothing() {
+ val detectedEdge =
+ swipeHorizontallyFrom(
+ x = screenWidth / 3,
+ y = screenHeight - (edgeSize - 1),
+ )
+ assertThat(detectedEdge).isNull()
+ }
+
+ @Test
+ fun source_swipeHorizontallyOnLeft_detectsLeft() {
+ val detectedEdge =
+ swipeHorizontallyFrom(
+ x = edgeSize - 1,
+ y = screenHeight / 2,
+ )
+ assertThat(detectedEdge).isEqualTo(Left)
+ }
+
+ @Test
+ fun source_swipeVerticallyOnLeft_detectsNothing() {
+ val detectedEdge =
+ swipeVerticallyFrom(
+ x = edgeSize - 1,
+ y = screenHeight / 2,
+ )
+ assertThat(detectedEdge).isNull()
+ }
+
+ @Test
+ fun source_swipeHorizontallyOnRight_detectsRight() {
+ val detectedEdge =
+ swipeHorizontallyFrom(
+ x = screenWidth - edgeSize + 1,
+ y = screenHeight / 2,
+ )
+ assertThat(detectedEdge).isEqualTo(Right)
+ }
+
+ @Test
+ fun source_swipeVerticallyOnRight_detectsNothing() {
+ val detectedEdge =
+ swipeVerticallyFrom(
+ x = screenWidth - edgeSize + 1,
+ y = screenHeight / 2,
+ )
+ assertThat(detectedEdge).isNull()
+ }
+
+ @Test
+ fun resolve_startInLtr_resolvesLeft() {
+ val resolvedEdge = Start.resolve(LayoutDirection.Ltr)
+ assertThat(resolvedEdge).isEqualTo(Left)
+ }
+
+ @Test
+ fun resolve_startInRtl_resolvesRight() {
+ val resolvedEdge = Start.resolve(LayoutDirection.Rtl)
+ assertThat(resolvedEdge).isEqualTo(Right)
+ }
+
+ @Test
+ fun resolve_endInLtr_resolvesRight() {
+ val resolvedEdge = End.resolve(LayoutDirection.Ltr)
+ assertThat(resolvedEdge).isEqualTo(Right)
+ }
+
+ @Test
+ fun resolve_endInRtl_resolvesLeft() {
+ val resolvedEdge = End.resolve(LayoutDirection.Rtl)
+ assertThat(resolvedEdge).isEqualTo(Left)
+ }
+
+ @Test
+ fun resolve_topStartInLtr_resolvesTopLeft() {
+ val resolvedEdge = TopStart.resolve(LayoutDirection.Ltr)
+ assertThat(resolvedEdge).isEqualTo(TopLeft)
+ }
+
+ @Test
+ fun resolve_topStartInRtl_resolvesTopRight() {
+ val resolvedEdge = TopStart.resolve(LayoutDirection.Rtl)
+ assertThat(resolvedEdge).isEqualTo(TopRight)
+ }
+
+ @Test
+ fun resolve_topEndInLtr_resolvesTopRight() {
+ val resolvedEdge = TopEnd.resolve(LayoutDirection.Ltr)
+ assertThat(resolvedEdge).isEqualTo(TopRight)
+ }
+
+ @Test
+ fun resolve_topEndInRtl_resolvesTopLeft() {
+ val resolvedEdge = TopEnd.resolve(LayoutDirection.Rtl)
+ assertThat(resolvedEdge).isEqualTo(TopLeft)
+ }
+
+ private fun swipeVerticallyFrom(x: Int, y: Int): SceneContainerEdge.Resolved? {
+ return swipeFrom(x, y, Orientation.Vertical)
+ }
+
+ private fun swipeHorizontallyFrom(x: Int, y: Int): SceneContainerEdge.Resolved? {
+ return swipeFrom(x, y, Orientation.Horizontal)
+ }
+
+ private fun swipeFrom(x: Int, y: Int, orientation: Orientation): SceneContainerEdge.Resolved? {
+ return underTest.source(
+ layoutSize = IntSize(width = screenWidth, height = screenHeight),
+ position = IntOffset(x, y),
+ density = Density(1f),
+ orientation = orientation,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
index 900f2a4..972afb5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
@@ -42,12 +42,12 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-class SceneActionsViewModelTest : SysuiTestCase() {
+class UserActionsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val underTest = FakeSceneActionsViewModel()
+ private val underTest = FakeUserActionsViewModel()
@Test
fun actions_emptyBeforeActivation() =
@@ -115,7 +115,7 @@
assertThat(actions).isEmpty()
}
- private class FakeSceneActionsViewModel : SceneActionsViewModel() {
+ private class FakeUserActionsViewModel : UserActionsViewModel() {
val upstream = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index 3283ea1..d163abf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -19,12 +19,9 @@
import android.app.StatusBarManager.DISABLE2_NONE
import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.parameterizeSceneContainerFlag
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -39,10 +36,7 @@
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.shade.shared.flag.DualShade
-import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
import com.android.systemui.statusbar.phone.dozeParameters
@@ -66,18 +60,17 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
class ShadeInteractorImplTest(flags: FlagsParameterization) : SysuiTestCase() {
- val kosmos = testKosmos()
- val testScope = kosmos.testScope
- val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
- val deviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository }
- val disableFlagsRepository by lazy { kosmos.fakeDisableFlagsRepository }
- val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
- val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
- val powerRepository by lazy { kosmos.fakePowerRepository }
- val shadeTestUtil by lazy { kosmos.shadeTestUtil }
- val userRepository by lazy { kosmos.fakeUserRepository }
- val userSetupRepository by lazy { kosmos.fakeUserSetupRepository }
- val dozeParameters by lazy { kosmos.dozeParameters }
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val deviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository }
+ private val disableFlagsRepository by lazy { kosmos.fakeDisableFlagsRepository }
+ private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+ private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+ private val powerRepository by lazy { kosmos.fakePowerRepository }
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+ private val userRepository by lazy { kosmos.fakeUserRepository }
+ private val userSetupRepository by lazy { kosmos.fakeUserSetupRepository }
+ private val dozeParameters by lazy { kosmos.dozeParameters }
lateinit var underTest: ShadeInteractorImpl
@@ -142,9 +135,7 @@
userSetupRepository.setUserSetUp(true)
disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NOTIFICATION_SHADE,
- )
+ DisableFlagsModel(disable2 = DISABLE2_NOTIFICATION_SHADE)
val actual by collectLastValue(underTest.isExpandToQsEnabled)
@@ -158,9 +149,7 @@
userSetupRepository.setUserSetUp(true)
disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_QUICK_SETTINGS,
- )
+ DisableFlagsModel(disable2 = DISABLE2_QUICK_SETTINGS)
val actual by collectLastValue(underTest.isExpandToQsEnabled)
assertThat(actual).isFalse()
@@ -171,10 +160,7 @@
testScope.runTest {
deviceProvisioningRepository.setDeviceProvisioned(true)
userSetupRepository.setUserSetUp(true)
- disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NONE,
- )
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
keyguardRepository.setIsDozing(true)
@@ -188,10 +174,7 @@
testScope.runTest {
deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
- disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NONE,
- )
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
userSetupRepository.setUserSetUp(true)
@@ -205,10 +188,7 @@
testScope.runTest {
deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
- disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NONE,
- )
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = false))
@@ -222,10 +202,7 @@
testScope.runTest {
deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
- disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NONE,
- )
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
userSetupRepository.setUserSetUp(true)
val actual by collectLastValue(underTest.isExpandToQsEnabled)
@@ -250,10 +227,7 @@
testScope.runTest {
deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
- disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NONE,
- )
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
userSetupRepository.setUserSetUp(true)
val actual by collectLastValue(underTest.isExpandToQsEnabled)
@@ -262,17 +236,12 @@
// WHEN QS is disabled
disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_QUICK_SETTINGS,
- )
+ DisableFlagsModel(disable2 = DISABLE2_QUICK_SETTINGS)
// THEN expand is disabled
assertThat(actual).isFalse()
// WHEN QS is enabled
- disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NONE,
- )
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
// THEN expand is enabled
assertThat(actual).isTrue()
}
@@ -282,10 +251,7 @@
testScope.runTest {
deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
- disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NONE,
- )
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
userSetupRepository.setUserSetUp(true)
val actual by collectLastValue(underTest.isExpandToQsEnabled)
@@ -359,9 +325,7 @@
)
)
keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(
- to = DozeStateModel.DOZE_AOD,
- )
+ DozeTransitionModel(to = DozeStateModel.DOZE_AOD)
)
val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
assertThat(isShadeTouchable).isFalse()
@@ -385,9 +349,7 @@
)
)
keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(
- to = DozeStateModel.DOZE_PULSING,
- )
+ DozeTransitionModel(to = DozeStateModel.DOZE_PULSING)
)
val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
assertThat(isShadeTouchable).isTrue()
@@ -450,51 +412,9 @@
lastSleepReason = WakeSleepReason.OTHER,
)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
assertThat(isShadeTouchable).isTrue()
}
-
- @Test
- @DisableFlags(DualShade.FLAG_NAME)
- fun legacyShadeMode_narrowScreen_singleShade() =
- testScope.runTest {
- val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(false)
-
- assertThat(shadeMode).isEqualTo(ShadeMode.Single)
- }
-
- @Test
- @DisableFlags(DualShade.FLAG_NAME)
- fun legacyShadeMode_wideScreen_splitShade() =
- testScope.runTest {
- val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(true)
-
- assertThat(shadeMode).isEqualTo(ShadeMode.Split)
- }
-
- @Test
- @EnableFlags(DualShade.FLAG_NAME)
- fun shadeMode_wideScreen_isDual() =
- testScope.runTest {
- val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(true)
-
- assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
- }
-
- @Test
- @EnableFlags(DualShade.FLAG_NAME)
- fun shadeMode_narrowScreen_isDual() =
- testScope.runTest {
- val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(false)
-
- assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
new file mode 100644
index 0000000..2a2817b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.shade.domain.interactor
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShadeModeInteractorImplTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private lateinit var underTest: ShadeModeInteractor
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.shadeModeInteractor
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun legacyShadeMode_narrowScreen_singleShade() =
+ testScope.runTest {
+ val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.shadeRepository.setShadeLayoutWide(false)
+
+ assertThat(shadeMode).isEqualTo(ShadeMode.Single)
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun legacyShadeMode_wideScreen_splitShade() =
+ testScope.runTest {
+ val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.shadeRepository.setShadeLayoutWide(true)
+
+ assertThat(shadeMode).isEqualTo(ShadeMode.Split)
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun shadeMode_wideScreen_isDual() =
+ testScope.runTest {
+ val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.shadeRepository.setShadeLayoutWide(true)
+
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun shadeMode_narrowScreen_isDual() =
+ testScope.runTest {
+ val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.shadeRepository.setShadeLayoutWide(false)
+
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
+ }
+
+ @Test
+ fun getTopEdgeSplitFraction_narrowScreen_splitInHalf() =
+ testScope.runTest {
+ // Ensure isShadeLayoutWide is collected.
+ val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
+ kosmos.shadeRepository.setShadeLayoutWide(false)
+
+ assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
+ }
+
+ @Test
+ fun getTopEdgeSplitFraction_wideScreen_leftSideLarger() =
+ testScope.runTest {
+ // Ensure isShadeLayoutWide is collected.
+ val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
+ kosmos.shadeRepository.setShadeLayoutWide(true)
+
+ assertThat(underTest.getTopEdgeSplitFraction()).isGreaterThan(0.5f)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
index 8b97739..f5022b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
@@ -16,18 +16,28 @@
package com.android.systemui.shade.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -150,4 +160,90 @@
)
assertThat(isKeyguardOccluded).isTrue()
}
+
+ @Test
+ @EnableSceneContainer
+ fun withSceneContainer_bouncerShowing_providesTheCorrectState() =
+ testScope.runTest {
+ val bouncerShowing by collectLastValue(underTest.isBouncerShowing)
+
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+ kosmos.sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+ assertThat(bouncerShowing).isFalse()
+
+ transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
+ runCurrent()
+ assertThat(bouncerShowing).isTrue()
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER)
+ fun withComposeBouncer_bouncerShowing_providesTheCorrectState() =
+ testScope.runTest {
+ val bouncerShowing by collectLastValue(underTest.isBouncerShowing)
+
+ kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = false)
+ runCurrent()
+ assertThat(bouncerShowing).isFalse()
+
+ kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
+ runCurrent()
+ assertThat(bouncerShowing).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun withSceneContainer_doesBouncerRequireIme_providesTheCorrectState() =
+ testScope.runTest {
+ val bouncerRequiresIme by collectLastValue(underTest.doesBouncerRequireIme)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Bouncer)
+ )
+ kosmos.sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+ assertThat(bouncerRequiresIme).isFalse()
+
+ // go back to lockscreen
+ transitionState.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+ runCurrent()
+
+ // change auth method
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ // go back to bouncer
+ transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
+ runCurrent()
+ assertThat(bouncerRequiresIme).isTrue()
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER)
+ fun withComposeBouncer_doesBouncerRequireIme_providesTheCorrectState() =
+ testScope.runTest {
+ val bouncerRequiresIme by collectLastValue(underTest.doesBouncerRequireIme)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+
+ kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
+ runCurrent()
+ assertThat(bouncerRequiresIme).isFalse()
+
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
+ runCurrent()
+ assertThat(bouncerRequiresIme).isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt
deleted file mode 100644
index 3f087b4..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt
+++ /dev/null
@@ -1,170 +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.shade.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-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
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
[email protected]
-@EnableSceneContainer
-class OverlayShadeViewModelTest : SysuiTestCase() {
-
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val sceneInteractor = kosmos.sceneInteractor
- private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
-
- private val underTest = kosmos.overlayShadeViewModel
-
- @Before
- fun setUp() {
- underTest.activateIn(testScope)
- }
-
- @Test
- fun backgroundScene_deviceLocked_lockscreen() =
- testScope.runTest {
- val backgroundScene by collectLastValue(underTest.backgroundScene)
-
- lockDevice()
-
- assertThat(backgroundScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun backgroundScene_deviceUnlocked_gone() =
- testScope.runTest {
- val backgroundScene by collectLastValue(underTest.backgroundScene)
-
- lockDevice()
- unlockDevice()
-
- assertThat(backgroundScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun backgroundScene_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
- testScope.runTest {
- val backgroundScene by collectLastValue(underTest.backgroundScene)
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- runCurrent()
-
- assertThat(backgroundScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun backgroundScene_authMethodSwipe_lockscreenDismissed_goesToGone() =
- testScope.runTest {
- val backgroundScene by collectLastValue(underTest.backgroundScene)
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor.changeScene(Scenes.Gone, "reason")
- runCurrent()
-
- assertThat(backgroundScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun onScrimClicked_onLockscreen_goesToLockscreen() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene)
- lockDevice()
- sceneInteractor.changeScene(Scenes.Bouncer, "reason")
- runCurrent()
- assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen)
-
- underTest.onScrimClicked()
-
- assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun onScrimClicked_deviceWasEntered_goesToGone() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene)
- val backgroundScene by collectLastValue(underTest.backgroundScene)
-
- lockDevice()
- unlockDevice()
- sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
- runCurrent()
- assertThat(backgroundScene).isEqualTo(Scenes.Gone)
- assertThat(currentScene).isNotEqualTo(Scenes.Gone)
-
- underTest.onScrimClicked()
-
- assertThat(currentScene).isEqualTo(Scenes.Gone)
- }
-
- private fun TestScope.lockDevice() {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
- sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- runCurrent()
- }
-
- private fun TestScope.unlockDevice() {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor.changeScene(Scenes.Gone, "reason")
- runCurrent()
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
similarity index 98%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
index a931e65..9f3e126e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
@@ -64,7 +64,7 @@
@TestableLooper.RunWithLooper
@EnableSceneContainer
@DisableFlags(DualShade.FLAG_NAME)
-class ShadeSceneActionsViewModelTest : SysuiTestCase() {
+class ShadeUserActionsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -72,7 +72,7 @@
private val shadeRepository by lazy { kosmos.shadeRepository }
private val qsSceneAdapter by lazy { kosmos.fakeQSSceneAdapter }
- private val underTest: ShadeSceneActionsViewModel by lazy { kosmos.shadeSceneActionsViewModel }
+ private val underTest: ShadeUserActionsViewModel by lazy { kosmos.shadeUserActionsViewModel }
@Before
fun setUp() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 75ecb2c..beba162 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -133,7 +133,8 @@
mVisibilityLocationProvider,
mVisualStabilityProvider,
mWakefulnessLifecycle,
- mKosmos.getCommunalInteractor(),
+ mKosmos.getCommunalSceneInteractor(),
+ mKosmos.getShadeInteractor(),
mKosmos.getKeyguardTransitionInteractor(),
mLogger);
mCoordinator.attach(mNotifPipeline);
@@ -561,11 +562,12 @@
@Test
public void testCommunalShowingWillNotSuppressReordering() {
- // GIVEN panel is expanded and communal is showing
+ // GIVEN panel is expanded, communal is showing, and QS is collapsed
setPulsing(false);
setFullyDozed(false);
setSleepy(false);
setPanelExpanded(true);
+ setQsExpanded(false);
setCommunalShowing(true);
// Reordering should be allowed
@@ -573,6 +575,20 @@
}
@Test
+ public void testQsExpandedOverCommunalWillSuppressReordering() {
+ // GIVEN panel is expanded and communal is showing, but QS is expanded
+ setPulsing(false);
+ setFullyDozed(false);
+ setSleepy(false);
+ setPanelExpanded(true);
+ setQsExpanded(true);
+ setCommunalShowing(true);
+
+ // Reordering should not be allowed
+ assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+ }
+
+ @Test
public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
// GIVEN visual stability is being maintained b/c panel is expanded
setPulsing(false);
@@ -631,7 +647,12 @@
new ObservableTransitionState.Idle(
isShowing ? CommunalScenes.Communal : CommunalScenes.Blank)
);
- mKosmos.getCommunalRepository().setTransitionState(showingFlow);
+ mKosmos.getCommunalSceneInteractor().setTransitionState(showingFlow);
+ mTestScope.getTestScheduler().runCurrent();
+ }
+
+ private void setQsExpanded(boolean isExpanded) {
+ mKosmos.getShadeRepository().setQsExpansion(isExpanded ? 1.0f : 0.0f);
mTestScope.getTestScheduler().runCurrent();
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 840aa92..26e1a4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.settingslib.notification.data.repository.updateNotificationPolicy
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
@@ -36,6 +37,7 @@
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
@@ -51,6 +53,7 @@
import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -153,7 +156,7 @@
fun shouldShowEmptyShadeView_trueWhenNoNotifs() =
testScope.runTest {
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -196,7 +199,7 @@
fun shouldShowEmptyShadeView_trueWhenQsExpandedInSplitShade() =
testScope.runTest {
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -217,7 +220,7 @@
fun shouldShowEmptyShadeView_trueWhenLockedShade() =
testScope.runTest {
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -315,7 +318,7 @@
@Test
fun shouldIncludeFooterView_trueWhenShade() =
testScope.runTest {
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
@@ -333,7 +336,7 @@
@Test
fun shouldIncludeFooterView_trueWhenLockedShade() =
testScope.runTest {
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
@@ -351,7 +354,7 @@
@Test
fun shouldIncludeFooterView_falseWhenKeyguard() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -366,7 +369,7 @@
@Test
fun shouldIncludeFooterView_falseWhenUserNotSetUp() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -384,7 +387,7 @@
@Test
fun shouldIncludeFooterView_falseWhenStartingToSleep() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -402,7 +405,7 @@
@Test
fun shouldIncludeFooterView_falseWhenQsExpandedDefault() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -421,7 +424,7 @@
@Test
fun shouldIncludeFooterView_trueWhenQsExpandedSplitShade() =
testScope.runTest {
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
@@ -444,7 +447,7 @@
@Test
fun shouldIncludeFooterView_falseWhenRemoteInputActive() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -462,7 +465,7 @@
@Test
fun shouldIncludeFooterView_animatesWhenShade() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -478,7 +481,7 @@
@Test
fun shouldIncludeFooterView_notAnimatingOnKeyguard() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -492,6 +495,22 @@
}
@Test
+ @EnableSceneContainer
+ fun shouldShowFooterView_falseWhenShadeIsClosed() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN shade is closed
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeTestUtil.setShadeExpansion(0f)
+ runCurrent()
+
+ // THEN footer is hidden
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ @DisableSceneContainer
fun shouldHideFooterView_trueWhenShadeIsClosed() =
testScope.runTest {
val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -506,6 +525,7 @@
}
@Test
+ @DisableSceneContainer
fun shouldHideFooterView_falseWhenShadeIsOpen() =
testScope.runTest {
val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -520,6 +540,7 @@
}
@Test
+ @DisableSceneContainer
fun shouldHideFooterView_falseWhenQSPartiallyOpen() =
testScope.runTest {
val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -642,4 +663,10 @@
assertThat(animationsEnabled).isTrue()
}
+
+ private fun TestScope.collectFooterViewVisibility() =
+ collectLastValue(
+ if (SceneContainerFlag.isEnabled) underTest.shouldShowFooterView
+ else underTest.shouldIncludeFooterView
+ )
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 3f97f0b..425f16e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -312,7 +312,7 @@
from = LOCKSCREEN,
to = GLANCEABLE_HUB,
value = 0f,
- )
+ ),
)
runCurrent()
@@ -321,7 +321,7 @@
Transition(
from = Scenes.Lockscreen,
to = Scenes.Communal,
- progress = flowOf(progress)
+ progress = flowOf(progress),
),
stateTransition =
TransitionStep(
@@ -329,7 +329,7 @@
from = LOCKSCREEN,
to = GLANCEABLE_HUB,
value = progress,
- )
+ ),
)
runCurrent()
@@ -344,7 +344,7 @@
from = LOCKSCREEN,
to = GLANCEABLE_HUB,
value = 1f,
- )
+ ),
)
assertThat(alpha).isEqualTo(0f)
@@ -378,7 +378,7 @@
from = DREAMING,
to = GLANCEABLE_HUB,
value = 0f,
- )
+ ),
)
runCurrent()
kosmos.setTransition(
@@ -386,7 +386,7 @@
Transition(
from = Scenes.Lockscreen,
to = Scenes.Communal,
- progress = flowOf(progress)
+ progress = flowOf(progress),
),
stateTransition =
TransitionStep(
@@ -394,7 +394,7 @@
from = DREAMING,
to = GLANCEABLE_HUB,
value = progress,
- )
+ ),
)
runCurrent()
// Keep notifications hidden during the transition from dream to hub
@@ -409,7 +409,7 @@
from = DREAMING,
to = GLANCEABLE_HUB,
value = 1f,
- )
+ ),
)
assertThat(alpha).isEqualTo(0f)
}
@@ -435,13 +435,13 @@
kosmos.setTransition(
sceneTransition = Idle(Scenes.Gone),
- stateTransition = TransitionStep(from = LOCKSCREEN, to = GONE)
+ stateTransition = TransitionStep(from = LOCKSCREEN, to = GONE),
)
assertThat(isOnLockscreen).isFalse()
kosmos.setTransition(
sceneTransition = Idle(Scenes.Lockscreen),
- stateTransition = TransitionStep(from = GONE, to = LOCKSCREEN)
+ stateTransition = TransitionStep(from = GONE, to = LOCKSCREEN),
)
assertThat(isOnLockscreen).isTrue()
// While progressing from lockscreen, should still be true
@@ -452,28 +452,20 @@
from = LOCKSCREEN,
to = GONE,
value = 0.8f,
- transitionState = TransitionState.RUNNING
- )
+ transitionState = TransitionState.RUNNING,
+ ),
)
assertThat(isOnLockscreen).isTrue()
kosmos.setTransition(
sceneTransition = Idle(Scenes.Lockscreen),
- stateTransition =
- TransitionStep(
- from = GONE,
- to = LOCKSCREEN,
- )
+ stateTransition = TransitionStep(from = GONE, to = LOCKSCREEN),
)
assertThat(isOnLockscreen).isTrue()
kosmos.setTransition(
sceneTransition = Idle(Scenes.Bouncer),
- stateTransition =
- TransitionStep(
- from = LOCKSCREEN,
- to = PRIMARY_BOUNCER,
- )
+ stateTransition = TransitionStep(from = LOCKSCREEN, to = PRIMARY_BOUNCER),
)
assertThat(isOnLockscreen).isTrue()
}
@@ -527,11 +519,7 @@
// Move to glanceable hub
kosmos.setTransition(
sceneTransition = Idle(Scenes.Communal),
- stateTransition =
- TransitionStep(
- from = LOCKSCREEN,
- to = GLANCEABLE_HUB,
- )
+ stateTransition = TransitionStep(from = LOCKSCREEN, to = GLANCEABLE_HUB),
)
assertThat(isOnGlanceableHubWithoutShade).isTrue()
@@ -553,11 +541,7 @@
shadeTestUtil.setLockscreenShadeExpansion(0f)
kosmos.setTransition(
sceneTransition = Idle(Scenes.Communal),
- stateTransition =
- TransitionStep(
- from = LOCKSCREEN,
- to = GLANCEABLE_HUB,
- )
+ stateTransition = TransitionStep(from = LOCKSCREEN, to = GLANCEABLE_HUB),
)
assertThat(isOnGlanceableHubWithoutShade).isTrue()
}
@@ -779,7 +763,7 @@
configurationRepository.setDimensionPixelSize(
R.dimen.keyguard_translate_distance_on_swipe_up,
- -100
+ -100,
)
configurationRepository.onAnyConfigurationChange()
@@ -800,7 +784,7 @@
configurationRepository.setDimensionPixelSize(
R.dimen.keyguard_translate_distance_on_swipe_up,
- -100
+ -100,
)
configurationRepository.onAnyConfigurationChange()
@@ -839,7 +823,8 @@
fun alphaOnFullQsExpansion() =
testScope.runTest {
val viewState = ViewStateAccessor()
- val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+ val alpha by
+ collectLastValue(underTest.keyguardAlpha(viewState, testScope.backgroundScope))
showLockscreenWithQSExpanded()
@@ -856,12 +841,15 @@
@Test
@BrokenWithSceneContainer(330311871)
- fun alphaDoesNotUpdateWhileGoneTransitionIsRunning() =
+ fun alphaWhenGoneIsSetToOne() =
testScope.runTest {
val viewState = ViewStateAccessor()
- val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+ val alpha by
+ collectLastValue(underTest.keyguardAlpha(viewState, testScope.backgroundScope))
showLockscreen()
+ assertThat(alpha).isEqualTo(1f)
+
// GONE transition gets to 90% complete
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
@@ -881,65 +869,23 @@
)
)
runCurrent()
-
- // At this point, alpha should be zero
- assertThat(alpha).isEqualTo(0f)
-
- // An attempt to override by the shade should be ignored
- shadeTestUtil.setQsExpansion(0.5f)
- assertThat(alpha).isEqualTo(0f)
- }
-
- @Test
- fun alphaDoesNotUpdateWhileOcclusionTransitionIsRunning() =
- testScope.runTest {
- val viewState = ViewStateAccessor()
- val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
-
- showLockscreen()
- // OCCLUDED transition gets to 90% complete
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = LOCKSCREEN,
- to = OCCLUDED,
- transitionState = TransitionState.STARTED,
- value = 0f,
- )
- )
- runCurrent()
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = LOCKSCREEN,
- to = OCCLUDED,
- transitionState = TransitionState.RUNNING,
- value = 0.9f,
- )
- )
- runCurrent()
-
- // At this point, alpha should be zero
- assertThat(alpha).isEqualTo(0f)
-
- // An attempt to override by the shade should be ignored
- shadeTestUtil.setQsExpansion(0.5f)
- assertThat(alpha).isEqualTo(0f)
- }
-
- @Test
- fun alphaWhenGoneIsSetToOne() =
- testScope.runTest {
- val viewState = ViewStateAccessor()
- val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
-
- showLockscreen()
-
- keyguardTransitionRepository.sendTransitionSteps(
- from = LOCKSCREEN,
- to = GONE,
- testScope
- )
+ // Change in state should not immediately set value to 1f. Should wait for
+ // transition to complete
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ // Transition is active, and NSSL should be nearly faded out
+ assertThat(alpha).isLessThan(0.5f)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = LOCKSCREEN,
+ to = GONE,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+ )
+ runCurrent()
+ // Should reset to 1f
assertThat(alpha).isEqualTo(1f)
}
@@ -978,11 +924,7 @@
assertThat(fadeIn[0]).isEqualTo(false)
// ... then user hits power to go to AOD
- keyguardTransitionRepository.sendTransitionSteps(
- from = LOCKSCREEN,
- to = AOD,
- testScope,
- )
+ keyguardTransitionRepository.sendTransitionSteps(from = LOCKSCREEN, to = AOD, testScope)
// ... followed by a shade collapse
showLockscreen()
// ... does not trigger a fade in
@@ -994,7 +936,8 @@
fun alpha_isZero_fromPrimaryBouncerToGoneWhileCommunalSceneVisible() =
testScope.runTest {
val viewState = ViewStateAccessor()
- val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+ val alpha by
+ collectLastValue(underTest.keyguardAlpha(viewState, testScope.backgroundScope))
showPrimaryBouncer()
showCommunalScene()
@@ -1039,7 +982,7 @@
from = PRIMARY_BOUNCER,
to = GONE,
transitionState = TransitionState.FINISHED,
- value = 1f
+ value = 1f,
)
)
runCurrent()
@@ -1052,7 +995,8 @@
fun alpha_fromPrimaryBouncerToGoneWhenCommunalSceneNotVisible() =
testScope.runTest {
val viewState = ViewStateAccessor()
- val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+ val alpha by
+ collectLastValue(underTest.keyguardAlpha(viewState, testScope.backgroundScope))
showPrimaryBouncer()
hideCommunalScene()
@@ -1095,7 +1039,7 @@
from = PRIMARY_BOUNCER,
to = GONE,
transitionState = TransitionState.FINISHED,
- value = 1f
+ value = 1f,
)
)
runCurrent()
@@ -1107,7 +1051,8 @@
fun alpha_isZero_fromAlternateBouncerToGoneWhileCommunalSceneVisible() =
testScope.runTest {
val viewState = ViewStateAccessor()
- val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+ val alpha by
+ collectLastValue(underTest.keyguardAlpha(viewState, testScope.backgroundScope))
showAlternateBouncer()
showCommunalScene()
@@ -1152,7 +1097,7 @@
from = ALTERNATE_BOUNCER,
to = GONE,
transitionState = TransitionState.FINISHED,
- value = 1f
+ value = 1f,
)
)
runCurrent()
@@ -1165,7 +1110,8 @@
fun alpha_fromAlternateBouncerToGoneWhenCommunalSceneNotVisible() =
testScope.runTest {
val viewState = ViewStateAccessor()
- val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+ val alpha by
+ collectLastValue(underTest.keyguardAlpha(viewState, testScope.backgroundScope))
showAlternateBouncer()
hideCommunalScene()
@@ -1208,7 +1154,7 @@
from = ALTERNATE_BOUNCER,
to = GONE,
transitionState = TransitionState.FINISHED,
- value = 1f
+ value = 1f,
)
)
runCurrent()
@@ -1221,11 +1167,7 @@
runCurrent()
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
runCurrent()
- keyguardTransitionRepository.sendTransitionSteps(
- from = AOD,
- to = LOCKSCREEN,
- testScope,
- )
+ keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)
}
private suspend fun TestScope.showDream() {
@@ -1247,11 +1189,7 @@
runCurrent()
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
runCurrent()
- keyguardTransitionRepository.sendTransitionSteps(
- from = AOD,
- to = LOCKSCREEN,
- testScope,
- )
+ keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)
}
private suspend fun TestScope.showLockscreenWithQSExpanded() {
@@ -1260,11 +1198,7 @@
runCurrent()
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
runCurrent()
- keyguardTransitionRepository.sendTransitionSteps(
- from = AOD,
- to = LOCKSCREEN,
- testScope,
- )
+ keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)
}
private suspend fun TestScope.showPrimaryBouncer() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index a6fdd03..b5dbc3f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -22,8 +22,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
@@ -73,11 +71,6 @@
private val demoModelFlow = MutableStateFlow<FakeWifiEventModel?>(null)
private val mainExecutor = FakeExecutor(FakeSystemClock())
- private val featureFlags =
- FakeFeatureFlagsClassic().also {
- it.set(Flags.INSTANT_TETHER, true)
- it.set(Flags.WIFI_SECONDARY_NETWORKS, true)
- }
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -93,7 +86,6 @@
realImpl =
WifiRepositoryImpl(
- featureFlags,
testScope.backgroundScope,
mainExecutor,
testDispatcher,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 84c728c..c0a1592 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -78,7 +78,7 @@
@Test
fun ssid_inactiveNetwork_outputsNull() =
testScope.runTest {
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive())
var latest: String? = "default"
val job = underTest.ssid.onEach { latest = it }.launchIn(this)
@@ -93,7 +93,7 @@
fun ssid_carrierMergedNetwork_outputsNull() =
testScope.runTest {
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(networkId = 1, subscriptionId = 2, level = 1)
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = 2, level = 1)
)
var latest: String? = "default"
@@ -106,53 +106,10 @@
}
@Test
- fun ssid_isPasspointAccessPoint_outputsPasspointName() =
- testScope.runTest {
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
- networkId = 1,
- level = 1,
- isPasspointAccessPoint = true,
- passpointProviderFriendlyName = "friendly",
- )
- )
-
- var latest: String? = null
- val job = underTest.ssid.onEach { latest = it }.launchIn(this)
- runCurrent()
-
- assertThat(latest).isEqualTo("friendly")
-
- job.cancel()
- }
-
- @Test
- fun ssid_isOnlineSignUpForPasspoint_outputsPasspointName() =
- testScope.runTest {
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
- networkId = 1,
- level = 1,
- isOnlineSignUpForPasspointAccessPoint = true,
- passpointProviderFriendlyName = "friendly",
- )
- )
-
- var latest: String? = null
- val job = underTest.ssid.onEach { latest = it }.launchIn(this)
- runCurrent()
-
- assertThat(latest).isEqualTo("friendly")
-
- job.cancel()
- }
-
- @Test
fun ssid_unknownSsid_outputsNull() =
testScope.runTest {
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
level = 1,
ssid = WifiManager.UNKNOWN_SSID,
)
@@ -171,8 +128,7 @@
fun ssid_validSsid_outputsSsid() =
testScope.runTest {
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
level = 1,
ssid = "MyAwesomeWifiNetwork",
)
@@ -233,12 +189,10 @@
fun wifiNetwork_matchesRepoWifiNetwork() =
testScope.runTest {
val wifiNetwork =
- WifiNetworkModel.Active(
- networkId = 45,
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 3,
ssid = "AB",
- passpointProviderFriendlyName = "friendly"
)
wifiRepository.setWifiNetwork(wifiNetwork)
@@ -309,7 +263,7 @@
val latest by collectLastValue(underTest.areNetworksAvailable)
wifiRepository.wifiScanResults.value = emptyList()
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive())
assertThat(latest).isFalse()
}
@@ -326,7 +280,7 @@
WifiScanEntry(ssid = "ssid 3"),
)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive())
assertThat(latest).isTrue()
}
@@ -344,9 +298,8 @@
)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
ssid = "ssid 2",
- networkId = 1,
level = 2,
)
)
@@ -365,9 +318,8 @@
)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
ssid = "ssid 2",
- networkId = 1,
level = 2,
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 1237347..141e304 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -25,14 +25,14 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.statusbar.connectivity.WifiIcons
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
@@ -58,10 +59,11 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class WifiViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private lateinit var underTest: WifiViewModel
- @Mock private lateinit var tableLogBuffer: TableLogBuffer
+ private val tableLogBuffer = logcatTableLogBuffer(kosmos, "WifiViewModelTest")
@Mock private lateinit var connectivityConstants: ConnectivityConstants
@Mock private lateinit var wifiConstants: WifiConstants
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -86,7 +88,7 @@
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
- FakeMobileConnectionsRepository(),
+ kosmos.fakeMobileConnectionsRepository,
),
tableLogBuffer,
testScope.backgroundScope,
@@ -113,9 +115,7 @@
val latestKeyguard by collectLastValue(keyguard.wifiIcon)
val latestQs by collectLastValue(qs.wifiIcon)
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1)
- )
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(isValidated = true, level = 1))
assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
assertThat(latestHome).isEqualTo(latestKeyguard)
@@ -129,8 +129,7 @@
// Even WHEN the network has a valid hotspot type
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
- NETWORK_ID,
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 1,
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.LAPTOP,
@@ -192,9 +191,7 @@
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1)
- )
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(ssid = null, level = 1))
val activityIn by collectLastValue(underTest.isActivityInViewVisible)
val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
@@ -217,9 +214,7 @@
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1)
- )
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(ssid = null, level = 1))
val activityIn by collectLastValue(underTest.isActivityInViewVisible)
val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
@@ -468,8 +463,6 @@
}
companion object {
- private const val NETWORK_ID = 2
- private val ACTIVE_VALID_WIFI_NETWORK =
- WifiNetworkModel.Active(NETWORK_ID, ssid = "AB", level = 1)
+ private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active.of(ssid = "AB", level = 1)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
index 20d3a7b6..fb32855 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -22,8 +22,10 @@
import android.provider.Settings.Secure.ZEN_DURATION
import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
+import android.service.notification.SystemZenRules
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.R
import com.android.settingslib.notification.data.repository.updateNotificationPolicy
import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.systemui.SysuiTestCase
@@ -220,30 +222,116 @@
}
@Test
- fun mainActiveMode_returnsMainActiveMode() =
+ fun activeModes_computesMainActiveMode() =
testScope.runTest {
- val mainActiveMode by collectLastValue(underTest.mainActiveMode)
+ val activeModes by collectLastValue(underTest.activeModes)
zenModeRepository.addMode(id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME)
zenModeRepository.addMode(id = "Other", type = AutomaticZenRule.TYPE_OTHER)
runCurrent()
+ assertThat(activeModes?.modeNames).hasSize(0)
+ assertThat(activeModes?.mainMode).isNull()
+
+ zenModeRepository.activateMode("Other")
+ runCurrent()
+ assertThat(activeModes?.modeNames).containsExactly("Mode Other")
+ assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Other")
+
+ zenModeRepository.activateMode("Bedtime")
+ runCurrent()
+ assertThat(activeModes?.modeNames)
+ .containsExactly("Mode Bedtime", "Mode Other")
+ .inOrder()
+ assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime")
+
+ zenModeRepository.deactivateMode("Other")
+ runCurrent()
+ assertThat(activeModes?.modeNames).containsExactly("Mode Bedtime")
+ assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime")
+
+ zenModeRepository.deactivateMode("Bedtime")
+ runCurrent()
+ assertThat(activeModes?.modeNames).hasSize(0)
+ assertThat(activeModes?.mainMode).isNull()
+ }
+
+ @Test
+ fun getActiveModes_computesMainActiveMode() = runTest {
+ zenModeRepository.addMode(id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME)
+ zenModeRepository.addMode(id = "Other", type = AutomaticZenRule.TYPE_OTHER)
+
+ var activeModes = underTest.getActiveModes()
+ assertThat(activeModes.modeNames).hasSize(0)
+ assertThat(activeModes.mainMode).isNull()
+
+ zenModeRepository.activateMode("Other")
+ activeModes = underTest.getActiveModes()
+ assertThat(activeModes.modeNames).containsExactly("Mode Other")
+ assertThat(activeModes.mainMode?.name).isEqualTo("Mode Other")
+
+ zenModeRepository.activateMode("Bedtime")
+ activeModes = underTest.getActiveModes()
+ assertThat(activeModes.modeNames).containsExactly("Mode Bedtime", "Mode Other").inOrder()
+ assertThat(activeModes.mainMode?.name).isEqualTo("Mode Bedtime")
+
+ zenModeRepository.deactivateMode("Other")
+ activeModes = underTest.getActiveModes()
+ assertThat(activeModes.modeNames).containsExactly("Mode Bedtime")
+ assertThat(activeModes.mainMode?.name).isEqualTo("Mode Bedtime")
+
+ zenModeRepository.deactivateMode("Bedtime")
+ activeModes = underTest.getActiveModes()
+ assertThat(activeModes.modeNames).hasSize(0)
+ assertThat(activeModes.mainMode).isNull()
+ }
+
+ @Test
+ fun mainActiveMode_flows() =
+ testScope.runTest {
+ val mainActiveMode by collectLastValue(underTest.mainActiveMode)
+
+ zenModeRepository.addModes(
+ listOf(
+ TestModeBuilder()
+ .setId("Bedtime")
+ .setName("Mode Bedtime")
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setActive(false)
+ .setPackage(mContext.packageName)
+ .setIconResId(R.drawable.ic_zen_mode_type_bedtime)
+ .build(),
+ TestModeBuilder()
+ .setId("Other")
+ .setName("Mode Other")
+ .setType(AutomaticZenRule.TYPE_OTHER)
+ .setActive(false)
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setIconResId(R.drawable.ic_zen_mode_type_other)
+ .build(),
+ )
+ )
+
+ runCurrent()
assertThat(mainActiveMode).isNull()
zenModeRepository.activateMode("Other")
runCurrent()
- assertThat(mainActiveMode).isNotNull()
- assertThat(mainActiveMode!!.id).isEqualTo("Other")
+ assertThat(mainActiveMode?.name).isEqualTo("Mode Other")
+ assertThat(mainActiveMode?.icon?.key?.resId)
+ .isEqualTo(R.drawable.ic_zen_mode_type_other)
zenModeRepository.activateMode("Bedtime")
runCurrent()
- assertThat(mainActiveMode).isNotNull()
- assertThat(mainActiveMode!!.id).isEqualTo("Bedtime")
+ assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime")
+ assertThat(mainActiveMode?.icon?.key?.resId)
+ .isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
zenModeRepository.deactivateMode("Other")
runCurrent()
- assertThat(mainActiveMode).isNotNull()
- assertThat(mainActiveMode!!.id).isEqualTo("Bedtime")
+ assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime")
+ assertThat(mainActiveMode?.icon?.key?.resId)
+ .isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
zenModeRepository.deactivateMode("Bedtime")
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
index 33f379d..a0f6431 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
@@ -23,7 +23,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder
-import com.android.settingslib.notification.modes.ZenIconLoader
import com.android.settingslib.notification.modes.ZenMode
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -35,12 +34,10 @@
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogEventLogger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
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.clearInvocations
@@ -67,11 +64,6 @@
mockDialogEventLogger
)
- @Before
- fun setUp() {
- ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService()))
- }
-
@Test
fun tiles_filtersOutUserDisabledModes() =
testScope.runTest {
@@ -338,6 +330,83 @@
}
@Test
+ fun tiles_populatesFieldsForAccessibility() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+
+ repository.addModes(
+ listOf(
+ TestModeBuilder()
+ .setName("With description, inactive")
+ .setManualInvocationAllowed(true)
+ .setTriggerDescription("When the going gets tough")
+ .setActive(false)
+ .build(),
+ TestModeBuilder()
+ .setName("With description, active")
+ .setManualInvocationAllowed(true)
+ .setTriggerDescription("When in Rome")
+ .setActive(true)
+ .build(),
+ TestModeBuilder()
+ .setName("With description, needs setup")
+ .setManualInvocationAllowed(true)
+ .setTriggerDescription("When you find yourself in a hole")
+ .setEnabled(false, /* byUser= */ false)
+ .build(),
+ TestModeBuilder()
+ .setName("Without description, inactive")
+ .setManualInvocationAllowed(true)
+ .setTriggerDescription(null)
+ .setActive(false)
+ .build(),
+ TestModeBuilder()
+ .setName("Without description, active")
+ .setManualInvocationAllowed(true)
+ .setTriggerDescription(null)
+ .setActive(true)
+ .build(),
+ TestModeBuilder()
+ .setName("Without description, needs setup")
+ .setManualInvocationAllowed(true)
+ .setTriggerDescription(null)
+ .setEnabled(false, /* byUser= */ false)
+ .build(),
+ )
+ )
+ runCurrent()
+
+ assertThat(tiles!!).hasSize(6)
+ with(tiles?.elementAt(0)!!) {
+ assertThat(this.stateDescription).isEqualTo("Off")
+ assertThat(this.subtextDescription).isEqualTo("When the going gets tough")
+ }
+ with(tiles?.elementAt(1)!!) {
+ assertThat(this.stateDescription).isEqualTo("On")
+ assertThat(this.subtextDescription).isEqualTo("When in Rome")
+ }
+ with(tiles?.elementAt(2)!!) {
+ assertThat(this.stateDescription).isEqualTo("Off")
+ assertThat(this.subtextDescription).isEqualTo("Set up")
+ }
+ with(tiles?.elementAt(3)!!) {
+ assertThat(this.stateDescription).isEqualTo("Off")
+ assertThat(this.subtextDescription).isEmpty()
+ }
+ with(tiles?.elementAt(4)!!) {
+ assertThat(this.stateDescription).isEqualTo("On")
+ assertThat(this.subtextDescription).isEmpty()
+ }
+ with(tiles?.elementAt(5)!!) {
+ assertThat(this.stateDescription).isEqualTo("Off")
+ assertThat(this.subtextDescription).isEqualTo("Set up")
+ }
+
+ // All tiles have the same long click info
+ tiles!!.forEach { assertThat(it.onLongClickLabel).isEqualTo("Open settings") }
+ }
+
+ @Test
fun onClick_togglesTileState() =
testScope.runTest {
val tiles by collectLastValue(underTest.tiles)
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/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
index 0f56d0b..fa7f37c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
@@ -90,8 +90,9 @@
assertThat(model)
.isEqualTo(
MediaOutputComponentModel.Calling(
- AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
- false,
+ device = AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
+ isInAudioSharing = false,
+ canOpenAudioSwitcher = false,
)
)
}
@@ -101,6 +102,9 @@
fun hasSession_stateIs_MediaSession() =
with(kosmos) {
testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(
+ TestMediaDevicesFactory.builtInMediaDevice()
+ )
mediaControllerRepository.setActiveSessions(listOf(localMediaController))
val model by collectLastValue(underTest.mediaOutputModel.filterData())
@@ -113,6 +117,7 @@
assertThat(device)
.isEqualTo(AudioOutputDevice.BuiltIn("built_in_media", testIcon))
assertThat(isInAudioSharing).isFalse()
+ assertThat(canOpenAudioSwitcher).isTrue()
}
}
}
@@ -129,8 +134,9 @@
assertThat(model)
.isEqualTo(
MediaOutputComponentModel.Idle(
- AudioOutputDevice.BuiltIn("built_in_media", testIcon),
- true,
+ device = AudioOutputDevice.BuiltIn("built_in_media", testIcon),
+ isInAudioSharing = true,
+ canOpenAudioSwitcher = false,
)
)
}
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
index 509f022..84f39af 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
@@ -21,4 +21,6 @@
interface BcSmartspaceConfigPlugin {
/** Gets default date/weather disabled status. */
val isDefaultDateWeatherDisabled: Boolean
+ /** Gets if Smartspace should use ViewPager2 */
+ val isViewPager2Enabled: Boolean
}
diff --git a/packages/SystemUI/res/drawable/arrow_pointing_down.xml b/packages/SystemUI/res/drawable/arrow_pointing_down.xml
index be39683..ca573c7 100644
--- a/packages/SystemUI/res/drawable/arrow_pointing_down.xml
+++ b/packages/SystemUI/res/drawable/arrow_pointing_down.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?attr/colorControlNormal">
+ android:tint="?android:attr/textColorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" />
diff --git a/packages/SystemUI/res/drawable/brightness_bar.xml b/packages/SystemUI/res/drawable/brightness_bar.xml
index 2afe164..3d1c1fb 100644
--- a/packages/SystemUI/res/drawable/brightness_bar.xml
+++ b/packages/SystemUI/res/drawable/brightness_bar.xml
@@ -21,7 +21,7 @@
android:viewportHeight="48">
<path
android:pathData="M2,22L302,22A2,2 0,0 1,304 24L304,24A2,2 0,0 1,302 26L2,26A2,2 0,0 1,0 24L0,24A2,2 0,0 1,2 22z"
- android:fillColor="@color/brightness_slider_track"/>
+ android:fillColor="?androidprv:attr/customColorShadeInactive"/>
<path
android:pathData="M24,0L205.71,0A24,24 0,0 1,229.71 24L229.71,24A24,24 0,0 1,205.71 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"
android:fillColor="?attr/shadeActive"/>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index cae9d6b..ec15b10 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -15,6 +15,7 @@
~ limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:paddingMode="stack" >
<item android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
@@ -24,7 +25,7 @@
<shape>
<size android:height="@dimen/rounded_slider_track_width" />
<corners android:radius="@dimen/rounded_slider_track_corner_radius" />
- <solid android:color="@color/brightness_slider_track" />
+ <solid android:color="?androidprv:attr/customColorShadeInactive" />
</shape>
</inset>
</item>
diff --git a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
index e06bfdc..368fe82 100644
--- a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
@@ -52,7 +52,7 @@
<Button
android:id="@android:id/button1"
style="?android:attr/buttonBarPositiveButtonStyle"
- android:layout_marginStart="8dp"
+ android:layout_marginStart="@dimen/dialog_button_side_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.android.internal.widget.ButtonBarLayout>
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/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
index 3b3ed39..91cd019 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
@@ -215,17 +215,4 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
tools:srcCompat="@tools:sample/avatars" />
-
- <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
- android:id="@+id/biometric_icon_overlay"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_gravity="center"
- android:contentDescription="@null"
- android:scaleType="fitXY"
- android:importantForAccessibility="no"
- app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
- app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
- app:layout_constraintStart_toStartOf="@+id/biometric_icon"
- app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
index 2a00495..51117a7 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
@@ -40,19 +40,6 @@
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
- <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
- android:id="@+id/biometric_icon_overlay"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_gravity="center"
- android:contentDescription="@null"
- android:scaleType="fitXY"
- android:importantForAccessibility="no"
- app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
- app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
- app:layout_constraintStart_toStartOf="@+id/biometric_icon"
- app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
-
<ScrollView
android:id="@+id/scrollView"
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
index 59cfecc..e7a40d1 100644
--- a/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
+++ b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
@@ -16,7 +16,7 @@
<com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/status_bar_latest_event_content"
+ android:id="@*android:id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml b/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml
new file mode 100644
index 0000000..ca6d66a
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@*android:id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ android:tag="big"
+ >
+
+ <LinearLayout
+ android:id="@*android:id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@*android:dimen/notification_content_margin"
+ android:orientation="vertical"
+ >
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="top"
+ >
+
+ <include layout="@*android:layout/notification_template_header" />
+
+ <LinearLayout
+ android:id="@*android:id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+ android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
+ android:layout_marginTop="@*android:dimen/notification_content_margin_top"
+ android:orientation="vertical"
+ >
+
+ <include layout="@*android:layout/notification_template_part_line1" />
+
+ <include layout="@*android:layout/notification_template_text_multiline" />
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="@*android:dimen/notification_progress_bar_height"
+ android:layout_marginTop="@*android:dimen/notification_progress_margin_top"
+ layout="@*android:layout/notification_template_progress"
+ />
+ </LinearLayout>
+
+ <include layout="@*android:layout/notification_template_right_icon" />
+ </FrameLayout>
+
+ <ViewStub
+ android:layout="@*android:layout/notification_material_reply_text"
+ android:id="@*android:id/notification_material_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@*android:layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+ android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
+ android:layout_marginTop="@*android:dimen/notification_content_margin"
+ />
+
+ <include layout="@*android:layout/notification_material_action_list" />
+ </LinearLayout>
+</com.android.systemui.statusbar.notification.row.ui.view.EnRouteView>
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 154397d..690a89a 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -17,7 +17,6 @@
the chip. -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/ongoing_activity_chip"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|start"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index aa083ad..0533c7e 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -64,30 +64,27 @@
android:layout_height="wrap_content"
android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
style="@style/TextAppearance.Dialog.Body.Message"
- android:gravity="start"/>
+ android:gravity="start"
+ android:textAlignment="gravity"/>
<!-- Buttons -->
<com.android.internal.widget.ButtonBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:layout_marginTop="@dimen/screenrecord_buttons_margin_top">
+ android:layout_marginTop="@dimen/screenrecord_buttons_margin_top"
+ android:gravity="end">
<Button
android:id="@android:id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="0"
android:text="@string/cancel"
style="@style/Widget.Dialog.Button.BorderButton" />
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
<Button
android:id="@android:id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="0"
+ android:layout_marginStart="@dimen/dialog_button_side_margin"
android:text="@string/screenrecord_continue"
style="@style/Widget.Dialog.Button" />
</com.android.internal.widget.ButtonBarLayout>
diff --git a/packages/SystemUI/res/layout/shelf_action_chip.xml b/packages/SystemUI/res/layout/shelf_action_chip.xml
index c7606e4..1c65e36 100644
--- a/packages/SystemUI/res/layout/shelf_action_chip.xml
+++ b/packages/SystemUI/res/layout/shelf_action_chip.xml
@@ -28,6 +28,7 @@
<ImageView
android:id="@+id/overlay_action_chip_icon"
android:tint="?androidprv:attr/materialColorOnSecondary"
+ android:tintMode="src_in"
android:layout_width="@dimen/overlay_action_chip_icon_size"
android:layout_height="@dimen/overlay_action_chip_icon_size"/>
<TextView
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 4247c7e..32bcca1 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">
@@ -99,7 +99,12 @@
android:gravity="center_vertical|start"
/>
- <include layout="@layout/ongoing_activity_chip" />
+ <include layout="@layout/ongoing_activity_chip"
+ android:id="@+id/ongoing_activity_chip_primary"/>
+
+ <include layout="@layout/ongoing_activity_chip"
+ android:id="@+id/ongoing_activity_chip_secondary"
+ android:visibility="gone"/>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 7251f03..2a669de 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> en ander oop apps het hierdie skermskoot bespeur."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Voeg by nota"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Sluit skakel in"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Skermopnemer"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Verwerk tans skermopname"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Deurlopende kennisgewing vir \'n skermopnamesessie"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Sluimerskerm"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Moenie Steur Nie"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteitmodusse"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Geen saamgebinde toestelle beskikbaar nie"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tik om ’n toestel te koppel of ontkoppel"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gebruik Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Gekoppel"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Oudiodeling"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tik om oor te skakel of oudio te deel"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Maak Instellings oop"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ander toestel"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Wissel oorsig"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteitmodusse"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Instellings"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Die diens wat hierdie funksie verskaf, sal toegang hê tot al die inligting wat op jou skerm sigbaar is of op jou toestel gespeel word terwyl dit opneem of uitsaai. Dit sluit in inligting soos wagwoorde, betalingbesonderhede, foto’s, boodskappe en oudio wat jy speel."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Deel of neem ’n app op"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Deel jou skerm met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Deel een app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Deel hele skerm"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Deel een app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deel hele skerm"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Wanneer jy jou hele skerm deel, is enigiets op jou skerm sigbaar aan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Wanneer jy ’n app deel, is enigiets wat in die app wys of speel, sigbaar aan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deel skerm"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliet, goeie toestand"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliet, verbinding is beskikbaar"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelliet-SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Noodoproepe of SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Werkprofiel"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Pret vir party mense, maar nie vir almal nie"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Stelsel-UI-ontvanger gee jou ekstra maniere om die Android-gebruikerkoppelvlak in te stel en te pasmaak. Hierdie eksperimentele kenmerke kan in toekomstige uitreikings verander, breek of verdwyn. Gaan versigtig voort."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Teruggebaar"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Tuisgebaar"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Handelingsleutel"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 54fb216d..f7f9763 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> እና ሌሎች ክፍት መተግበሪያዎች ይህን ቅጽበታዊ ገፅ ዕይታ ለይተዋል።"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ወደ ማስታወሻ አክል"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"አገናኝ አካትት"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g><xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"የማያ መቅረጫ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"የማያ ገፅ ቀረጻን በማሰናዳት ላይ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ለአንድ የማያ ገፅ ቀረጻ ክፍለ-ጊዜ በመካሄድ ያለ ማሳወቂያ"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"የማያ ገፅ ማቆያ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ኤተርኔት"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"አትረብሽ"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ቅድሚያ ሁነታዎች"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ብሉቱዝ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ምንም የተጣመሩ መሣሪያዎች አይገኝም"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"መሣሪያን ለማገናኘት ወይም ግንኙነቱን ለማቋረጥ መታ ያድርጉ"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ብሉቱዝን ይጠቀሙ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ተገናኝቷል"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"የድምጽ ማጋራት"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ኦዲዮ ለመቀየር ወይም ለማጋራት መታ ያድርጉ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ተቀምጧል"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ግንኙነትን አቋርጥ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ያግብሩ"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ቅንብሮችን ክፈት"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ሌላ መሣሪያ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"አጠቃላይ እይታን ቀያይር"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ቅድሚያ ሁነታዎች"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ተከናውኗል"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ቅንብሮች"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"በርቷል"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ይህን ተግባር የሚያቀርበው አገልግሎት በእርስዎ ማያ ገጽ ላይ ለሚታየው ወይም በሚቀረጽበት ወይም cast በሚደረግበት ጊዜ በእርስዎ መሣሪያ ላይ ለሚጫወተው ሁሉም መረጃ መዳረሻ ይኖረዋል። ይህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ ፎቶዎች፣ መልዕክቶች እና እርስዎ የሚያጫውቱትን ኦዲዮ የመሳሰለ መረጃን ያካትታል።"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"መተግበሪያን ያጋሩ ወይም ይቅረጹ"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"ማያ ገፅዎን ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ያጋራሉ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"አንድ መተግበሪያ ያጋሩ"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"መላውን ማያ ገፅ ያጋሩ"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"አንድ መተግበሪያ ያጋሩ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"መላውን ማያ ገፅ ያጋሩ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"እርስዎ ሙሉ ማያ ገፅዎን ሲያጋሩ በማያ ገፅዎ ላይ ያለው ማንኛውም ነገር ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"መተግበሪያን ሲያጋሩ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ማያ ገፅ አጋራ"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ሳተላይት፣ ጥሩ ግንኙነት"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ሳተላይት፣ ግንኙነት አለ"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"ሳተላይት ኤስኦኤስ"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"የአደጋ ጥሪዎች ወይም ኤስኦኤስ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"የስራ መገለጫ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ለአንዳንዶች አስደሳች ቢሆንም ለሁሉም አይደለም"</string>
<string name="tuner_warning" msgid="1861736288458481650">"የስርዓት በይነገጽ መቃኛ የAndroid ተጠቃሚ በይነገጹን የሚነካኩበት እና የሚያበጁበት ተጨማሪ መንገዶች ይሰጠዎታል። እነዚህ የሙከራ ባህሪዎች ወደፊት በሚኖሩ ልቀቶች ላይ ሊለወጡ፣ ሊሰበሩ ወይም ሊጠፉ ይችላሉ። ከጥንቃቄ ጋር ወደፊት ይቀጥሉ።"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"የተመለስ ምልክት"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"የቤት ምልክት"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"የተግባር ቁልፍ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 6866ed7..5d6b908 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"رصَد تطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\" والتطبيقات المفتوحة الأخرى لقطة الشاشة هذه."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"إضافة إلى الملاحظة"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"تضمين الرابط"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"مسجّل الشاشة"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"جارٍ معالجة تسجيل الشاشة"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"إشعار مستمر لجلسة تسجيل شاشة"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"شاشة الاستراحة"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"عدم الإزعاج"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"الأوضاع ذات الأولوية"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوتوث"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"لا يتوفر أي أجهزة مقترنة"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"انقر للاتصال بجهاز أو قطع الاتصال به"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"استخدام البلوتوث"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متّصل"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"مشاركة الصوت"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"انقر لتبديل مصدر الصوت أو مشاركته"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"إلغاء الربط"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"تفعيل"</string>
@@ -384,13 +387,13 @@
<string name="qs_record_issue_start" msgid="2979831312582567056">"بدء"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"إيقاف"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"تقرير خطأ"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ما هو الجانب الذي تأثّر في تجربة استخدام الجهاز؟"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"بأي جانب من تجربة استخدام الجهاز تتعلّق المشكلة؟"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"اختيار نوع المشكلة"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"تسجيل الشاشة"</string>
<string name="performance" msgid="6552785217174378320">"الأداء"</string>
<string name="user_interface" msgid="3712869377953950887">"واجهة المستخدم"</string>
- <string name="thermal" msgid="6758074791325414831">"الأداء الحراري"</string>
- <string name="custom" msgid="3337456985275158299">"مخصّص"</string>
+ <string name="thermal" msgid="6758074791325414831">"ارتفاع حرارة الجهاز"</string>
+ <string name="custom" msgid="3337456985275158299">"الإعدادات المخصّصة"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"إعدادات التتبع المخصصة"</string>
<string name="restore_default" msgid="5259420807486239755">"استعادة الإعدادات التلقائية"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"فتح الإعدادات"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"جهاز آخر"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تبديل \"النظرة العامة\""</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"الأوضاع ذات الأولوية"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"تم"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"الإعدادات"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"مفعَّل"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ستتمكن الخدمة التي تقدّم هذه الوظيفة من الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهازك أثناء التسجيل أو البثّ. ويشمل ذلك معلومات، مثل كلمات المرور وتفاصيل الدفع والصور والرسائل والمقاطع الصوتية التي تشغِّلها."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"مشاركة محتوى تطبيق أو تسجيله"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"هل تريد مشاركة الشاشة مع تطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"؟"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"مشاركة تطبيق واحد"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"مشاركة الشاشة بأكملها"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"مشاركة تطبيق واحد"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"مشاركة الشاشة بأكملها"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"أثناء مشاركة محتوى الشاشة بالكامل، سيكون كل المحتوى المعروض على شاشتك مرئيًا لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"أثناء مشاركة محتوى تطبيق، سيكون كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق مرئيًا لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"مشاركة الشاشة"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"قمر صناعي، الاتصال جيد"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"قمر صناعي، الاتصال متوفّر"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"اتصالات الطوارئ بالقمر الصناعي"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"مكالمات الطوارئ أو ميزة \"اتصالات طوارئ بالقمر الصناعي\""</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ملف العمل"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"متعة للبعض وليس للجميع"</string>
<string name="tuner_warning" msgid="1861736288458481650">"توفر لك أداة ضبط واجهة مستخدم النظام طرقًا إضافية لتعديل واجهة مستخدم Android وتخصيصها. ويمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"إيماءة الرجوع"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"إيماءة الانتقال إلى الشاشة الرئيسية"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"مفتاح الإجراء"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 0c8ef43..2e8a552 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> আৰু আন খোলা এপ্সমূহে এই স্ক্ৰীনশ্বটটো চিনাক্ত কৰিছে।"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"টোকাত যোগ দিয়ক"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"লিংক অন্তৰ্ভুক্ত কৰক"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রীন ৰেকৰ্ডিঙৰ প্ৰক্ৰিয়াকৰণ হৈ আছে"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীন ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"স্ক্ৰীন ছেভাৰ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ইথাৰনেট"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"অসুবিধা নিদিব"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"অগ্ৰাধিকাৰপ্ৰাপ্ত ম’ড"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ব্লুটুথ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"কোনো যোৰা লগোৱা ডিভাইচ উপলব্ধ নহয়।"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ডিভাইচ সংযোগ কৰিবলৈ অথবা সংযোগ বিচ্ছিন্ন কৰিবলৈ টিপক"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যৱহাৰ কৰক"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"সংযুক্ত আছে"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"অডিঅ’ শ্বেয়াৰ কৰা"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"অডিঅ’ সলনি কৰিবলৈ বা শ্বেয়াৰ কৰিলৈ টিপক"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ছেভ কৰা হৈছে"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"সক্ৰিয় কৰক"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ছেটিং খোলক"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইচ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"অৱলোকন ট’গল কৰক"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"অগ্ৰাধিকাৰপ্ৰাপ্ত ম’ডসমূহ"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"কৰা হ’ল"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ছেটিং"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"অন আছে"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"এই সুবিধাটো প্ৰদান কৰা সেৱাটোৱে আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকৰ্ডিং অথবা কাষ্টিঙৰ সময়ত আপোনাৰ ডিভাইচত প্লে’ কৰা আটাইবোৰ তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, ফট’, বাৰ্তাসমূহ আৰু আপুনি প্লে’ কৰা অডিঅ’ৰ দৰে তথ্য অন্তৰ্ভুক্ত হয়।"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"এটা এপ্ শ্বেয়াৰ অথবা ৰেকৰ্ড কৰক"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ সৈতে আপোনাৰ স্ক্ৰীন শ্বেয়াৰ কৰিবনে?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"এটা এপ্ শ্বেয়াৰ কৰক"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"গোটেই স্ক্ৰীনখন শ্বেয়াৰ কৰক"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"এটা এপ্ শ্বেয়াৰ কৰক"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"গোটেই স্ক্ৰীনখন শ্বেয়াৰ কৰক"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"আপুনি আপোনাৰ গোটেই স্ক্ৰীনখন শ্বেয়াৰ কৰি থাকোঁতে, আপোনাৰ স্ক্ৰীনত থকা যিকোনো বস্তু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ত দৃশ্যমান হয়। সেয়ে পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"আপুনি কোনো এপ্ শ্বেয়াৰ কৰি থাকোঁতে সেই এপ্টোত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ত দৃশ্যমান হয়। সেয়ে পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"স্ক্ৰীন শ্বেয়াৰ কৰক"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"উপগ্ৰহ, ভাল সংযোগ"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"উপগ্ৰহ, সংযোগ উপলব্ধ"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"উপগ্ৰহ SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"জৰুৰীকালীন কল বা SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"কিছুমানৰ বাবে আমোদজনক হয় কিন্তু সকলোৰে বাবে নহয়"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"উভতি যাওক নিৰ্দেশ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশ"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"কাৰ্য কী"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index df3ecf7..b011ca64 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> və digər açıq tətbiqlər bu skrinşotu aşkarladı."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Qeydə əlavə edin"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Keçid daxil edin"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g><xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Ekran yazıcısı"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Ekran qoruyucu"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Narahat etməyin"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritet rejimləri"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Heç bir cütlənmiş cihaz əlçatan deyil"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toxunaraq cihaza qoşulun, yaxud əlaqəni ayırın"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-u açın"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Qoşulub"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio paylaşma"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dəyişmək və ya audio paylaşmaq üçün toxunun"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"əlaqəni kəsin"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivləşdirin"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ayarları açın"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Digər cihaz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"İcmala Keçin"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritet rejimləri"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hazırdır"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ayarlar"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Bu funksiyanı təmin edən xidmətin qeydəalma və ya yayım zamanı ekranda görünən, yaxud cihazda oxudulan məlumatlara girişi olacaq. Bura parol, ödəniş detalları, foto, mesaj və oxudulan audio kimi məlumatlar daxildir."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Tətbiq paylaşın və ya qeydə alın"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ekran <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilə paylaşılsın?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Bir tətbiq paylaşın"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Bütün ekranı paylaşın"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Bir tətbiq paylaşın"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bütün ekranı paylaşın"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Bütün ekranı paylaşdığınız zaman ekrandakı hər şey <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> üçün görünən olacaq. Parol, ödəniş məlumatı, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Tətbiq paylaşdığınız zaman həmin tətbiqdə göstərilən və ya işə salınan hər şey <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> üçün görünən olacaq. Parol, ödəniş məlumatı, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranı paylaşın"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Peyk, bağlantı yaxşıdır"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Peyk, bağlantı var"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Təcili peyk bağlantısı"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Təcili zənglər və ya SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"İş profili"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Hamı üçün deyil, bəziləri üçün əyləncəli"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner Android istifadəçi interfeysini dəyişdirmək və fərdiləşdirmək üçün Sizə ekstra yollar təklif edir."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geri jesti"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Əsas ekran jesti"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Əməliyyat düyməsi"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 9198710..c581fd5 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije su otkrile ovaj snimak ekrana."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj u belešku"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Uvrsti link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Snimač ekrana"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađujemo video snimka ekrana"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Obaveštenje o sesiji snimanja ekrana je aktivno"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Čuvar ekrana"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Eternet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne uznemiravaj"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni režimi"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nije dostupan nijedan upareni uređaj"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da biste povezali uređaj ili prekinuli vezu"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Deljenje zvuka"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dodirnite da biste prebacili ili delili zvuk"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinite vezu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivirajte"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori Podešavanja"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključi/isključi pregled"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni režimi"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Podešavanja"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Usluga koja pruža ovu funkciju će imati pristup svim informacijama koje se prikazuju na ekranu ili reprodukuju sa uređaja tokom snimanja ili prebacivanja. To obuhvata informacije poput lozinki, informacija o plaćanju, slika, poruka i zvuka koji puštate."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Delite ili snimite aplikaciju"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Želite da delite ekran sa aplikacijom <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Deli jednu aplikaciju"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Deli ceo ekran"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Deli jednu aplikaciju"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deli ceo ekran"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada delite ceo ekran, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidi sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada delite aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidi sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deli ekran"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, veza je dobra"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Hitna pomoć preko satelita"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hitni pozivi ili hitna pomoć"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Poslovni profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Tjuner za korisnički interfejs sistema vam pruža dodatne načine za podešavanje i prilagođavanje Android korisničkog interfejsa. Ove eksperimentalne funkcije mogu da se promene, otkažu ili nestanu u budućim izdanjima. Budite oprezni."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za vraćanje"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za početnu stranicu"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Taster radnji"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index c5c2912..31854f6 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> і іншыя адкрытыя праграмы выявілі гэты здымак экрана."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Дадаць у нататку"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Дадаць спасылку"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Запіс экрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Апрацоўваецца запіс экрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Бягучае апавяшчэнне для сеанса запісу экрана"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Экранная застаўка"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не турбаваць"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Прыярытэтныя рэжымы"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Няма даступных спалучаных прылад"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Націсніце, каб падключыць або адключыць прыладу"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Выкарыстоўваць Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Падключана"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Абагульванне аўдыя"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Націсніце, каб пераключыць або абагуліць аўдыя"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Захавана"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"адключыць"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"актываваць"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Адкрыць налады"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Іншая прылада"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Уключыць/выключыць агляд"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Прыярытэтныя рэжымы"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Гатова"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Налады"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Уключана"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Падчас запісу ці трансляцыі служба, якая забяспечвае работу гэтай функцыі, будзе мець доступ да ўсёй інфармацыі, адлюстраванай на экране вашай прылады, ці той, якая праз яе прайграецца. Гэтая інфармацыя ўключае паролі, плацежных рэквізітаў, фота, паведамленні і аўдыя, якое вы прайграяце."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Абагульванне або запіс праграмы"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Абагуліць экран з праграмай \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Абагуліць адну праграму"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Абагуліць увесь экран"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Абагуліць адну праграму"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Абагуліць увесь экран"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Калі вы абагульваеце ўвесь экран, праграма \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" можа бачыць усё, што адбываецца на экране. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Калі вы абагульваеце праграму, праграма \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" можа бачыць усё, што паказваецца ці прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Абагуліць экран"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спадарожнікавая сувязь, добрае падключэнне"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спадарожнікавая сувязь, падключэнне даступнае"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Экстраннае спадарожнікавае падключэнне"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Экстранныя выклікі або экстраннае спадарожнікавае падключэнне"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Працоўны профіль"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Цікава для некаторых, але не для ўсіх"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Наладка сістэмнага інтэрфейсу карыстальніка дае вам дадатковыя спосабы наладжвання і дапасоўвання карыстальніцкага інтэрфейсу Android. Гэтыя эксперыментальныя функцыі могуць змяніцца, перастаць працаваць або знікнуць у будучых версіях. Карыстайцеся з асцярожнасцю."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жэст для вяртання на папярэдні экран"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жэст для вяртання на галоўны экран"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Клавіша дзеяння"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 58f492e..64ecfd3 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> и други отворени приложения установиха заснемането на тази екранна снимка."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Добавяне към бележката"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Включване на връзката"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Запис на екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Записът на екрана се обработва"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущо известие за сесия за записване на екрана"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Скрийнсейвър"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не безпокойте"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Няма налични сдвоени устройства"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Докоснете, за да свържете устройство или да прекъснете връзката му"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Използване на Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Установена е връзка"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Споделяне на звука"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Докоснете, за да превключите или споделите аудио"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Запазено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекратяване на връзката"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активиране"</string>
@@ -390,7 +393,7 @@
<string name="performance" msgid="6552785217174378320">"Ефективност"</string>
<string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string>
<string name="thermal" msgid="6758074791325414831">"Температура"</string>
- <string name="custom" msgid="3337456985275158299">"Персонализирано"</string>
+ <string name="custom" msgid="3337456985275158299">"Персонализиране"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Настройки за персонализираната следа"</string>
<string name="restore_default" msgid="5259420807486239755">"Възстановяване на стандартната настройка"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Към настройките"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Друго устройство"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Превключване на общия преглед"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Настройки"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Вкл."</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Услугата, предоставяща тази функция, ще има достъп до цялата информация, която е видима на екрана или възпроизвеждана от устройството ви по време на записване или предаване. Това включва различна информация, като например пароли, подробности за начини на плащане, снимки, съобщения и възпроизвеждано аудио."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Споделяне или записване на приложение"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Да се сподели ли екранът ви с(ъс) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Споделяне на едно приложение"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Споделяне на целия екран"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Споделяне на едно приложение"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Споделяне на целия екран"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Когато споделяте целия си екран, всичко, което се показва на него, е видимо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Когато споделяте приложение, всичко, което се показва или възпроизвежда в него, е видимо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Споделяне на екрана"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Сателит, добра връзка"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Сателит, налице е връзка"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS чрез сателит"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Спешни обаждания или SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Потребителски профил в Work"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Забавно – но не за всички"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Тунерът на системния потребителски интерфейс ви предоставя допълнителни възможности за прецизиране и персонализиране на практическата работа с Android. Тези експериментални функции може да се променят, повредят или да изчезнат в бъдещите версии. Действайте внимателно."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест за връщане назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест за преминаване към началния екран"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Клавиш за действия"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index c9e24f0..f62ee9e 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> এবং খোলা থাকা অন্য অ্যাপ এই স্ক্রিনশট শনাক্ত করেছে।"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"নোটে যোগ করুন"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"লিঙ্ক যোগ করুন"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"স্ক্রিন রেকর্ডার"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রিন রেকর্ডিং প্রসেস হচ্ছে"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রিন রেকর্ডিং সেশন চলার বিজ্ঞপ্তি"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"স্ক্রিন সেভার"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ইথারনেট"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"বিরক্ত করবে না"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"প্রায়োরিটি মোড"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ব্লুটুথ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"চেনা কোনও ডিভাইস নেই"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"কোনও ডিভাইস কানেক্ট বা ডিসকানেক্ট করতে ট্যাপ করুন"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যবহার করুন"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"কানেক্ট করা আছে"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"অডিও শেয়ারিং"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"অডিও শেয়ার বা পরিবর্তন করতে ট্যাপ করুন"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ডিসকানেক্ট করুন"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"চালু করুন"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"সেটিংস খুলুন"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইস"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"\'এক নজরে\' বৈশিষ্ট্যটি চালু বা বন্ধ করুন"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"প্রায়োরিটি মোড"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"হয়ে গেছে"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"সেটিংস"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"চালু আছে"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"যে পরিষেবা এই ফাংশন প্রদান করছে, সেটি রেকর্ড বা কাস্ট করার সময় আপনার স্ক্রিনে দৃশ্যমান বা ডিভাইসে চালানো হয়েছে এমন সব তথ্য অ্যাক্সেস করতে পারবে। এর মধ্যে আপনার পাসওয়ার্ড, পেমেন্টের বিবরণ, ফটো, মেসেজ এবং আপনার চালানো অডিও সম্পর্কিত তথ্য রয়েছে।"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"কোনও অ্যাপ শেয়ার বা রেকর্ড করুন"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-এর সাথে আপনার স্ক্রিন শেয়ার করবেন?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"একটি অ্যাপ শেয়ার করুন"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"সম্পূর্ণ স্ক্রিন শেয়ার করুন"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"একটি অ্যাপ শেয়ার করুন"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"সম্পূর্ণ স্ক্রিন শেয়ার করুন"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"আপনার সম্পূর্ণ স্ক্রিন শেয়ার করার সময়, স্ক্রিনে থাকা সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> দেখতে পাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"কোনও অ্যাপ শেয়ার করার সময়, সেই অ্যাপে দেখা ও চালানো হয় এমন সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> দেখতে পাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"স্ক্রিন শেয়ার করুন"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"স্যাটেলাইট, ভালো কানেকশন"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"স্যাটেলাইট, কানেকশন উপলভ্য আছে"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"স্যাটেলাইট SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"জরুরি কল বা SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"কাজের প্রোফাইল"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"কিছু ব্যক্তির জন্য মজাদার কিন্তু সকলের জন্য নয়"</string>
<string name="tuner_warning" msgid="1861736288458481650">"এই পরীক্ষামূলক বৈশিষ্ট্যগুলি ভবিষ্যতের সংস্করণগুলির মধ্যে পরিবর্তিত, বিভাজিত এবং অদৃশ্য হয়ে যেতে পারে৷ সাবধানতার সাথে এগিয়ে যান৷ সিস্টেম UI টিউনার আপনাকে Android ব্যবহারকারী ইন্টারফেসের সূক্ষ্ম সমন্বয় এবং কাস্টমাইজ করার অতিরিক্ত উপায়গুলি প্রদান করে৷"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ফিরে যাওয়ার জেসচার"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"হোমপেজে যাওয়ার জেসচার"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"অ্যাকশন কী"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 8949567..51416ef 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije su otkrile ovaj snimak ekrana."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj u bilješku"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Uključi link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Snimač ekrana"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađivanje snimka ekrana"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Obavještenje za sesiju snimanja ekrana je u toku"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Čuvar ekrana"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne ometaj"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni načini rada"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nema dostupnih uparenih uređaja"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da povežete ili prekinete povezanost uređaja"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Dijeljenje zvuka"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dodirnite da uključite ili dijelite zvučni zapis"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekid veze"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori Postavke"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pregled uključivanja/isključivanja"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni načini rada"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Postavke"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Usluga koja pruža ovu funkciju će imati pristup svim informacijama koje su vidljive na ekranu ili koje se reproduciraju s uređaja tokom snimanja ili emitiranja. To uključuje informacije kao što su lozinke, detalji o plaćanju, fotografije, poruke i zvuk koji reproducirate."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Dijelite ili snimajte aplikaciju"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Dijeliti ekran s aplikacijom <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Dijeli jednu aplikaciju"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Dijeli cijeli ekran"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Dijeli jednu aplikaciju"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dijeli cijeli ekran"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada dijelite cijeli ekran, sve što je na ekranu će biti vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada dijelite aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji će biti vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dijeli ekran"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra veza"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Hitna pomoć putem satelita"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hitni pozivi ili pomoć"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Radni profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Podešavač za korisnički interfejs sistema vam omogućava dodatne načine da podesite i prilagodite Androidov interfejs. Ove eksperimentalne funkcije se u budućim verzijama mogu mijenjati, kvariti ili nestati. Budite oprezni."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za povratak"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za povratak na početni ekran"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tipka radnji"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 74cce98..182fe02 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -104,13 +104,14 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i altres aplicacions obertes han detectat aquesta captura de pantalla."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Afegeix a una nota"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Inclou l\'enllaç"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Gravació de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vols gravar la pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grava una aplicació"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grava tota la pantalla"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quan graves tota la pantalla, es grava tot el que es mostra en pantalla. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quan graves tota la pantalla, es grava tot el que es mostra en pantalla. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quan graves una aplicació, es grava tot el que es mostra o es reprodueix en aquesta aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grava la pantalla"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Tria una aplicació per gravar"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Estalvi de pantalla"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"No molestis"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaris"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hi ha dispositius vinculats disponibles"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca per connectar o desconnectar un dispositiu"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utilitza el Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connectat"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartició d\'àudio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toca per canviar o compartir l\'àudio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Desat"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconnecta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activa"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Obre Configuració"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Un altre dispositiu"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activa o desactiva Aplicacions recents"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaris"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fet"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuració"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"El servei que ofereix aquesta funció tindrà accés a tota la informació visible a la teva pantalla o que es reprodueix al dispositiu mentre graves o emets contingut, com ara les contrasenyes, les dades de pagament, les fotos, els missatges i àudio que reprodueixis."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Comparteix o grava una aplicació"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vols compartir la pantalla amb <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Comparteix una aplicació"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Comparteix tota la pantalla"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Comparteix una aplicació"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Comparteix tota la pantalla"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quan comparteixes tota la pantalla, qualsevol cosa que es mostra en pantalla és visible a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quan comparteixes una aplicació, qualsevol cosa que es mostra o que es reprodueix en aquesta aplicació és visible a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Comparteix la pantalla"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satèl·lit, bona connexió"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satèl·lit, connexió disponible"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS per satèl·lit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Trucades d\'emergència o SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de treball"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversió per a uns quants, però no per a tothom"</string>
<string name="tuner_warning" msgid="1861736288458481650">"El Personalitzador d\'interfície d\'usuari presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gest Enrere"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gest Inici"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla d\'acció"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 8c0571e..cebb175 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> a ostatní otevřené aplikace objevily tento snímek obrazovky."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Přidat do poznámky"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Zahrnout odkaz"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Nahrávání obrazovky"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Záznam obrazovky se zpracovává"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Spořič obrazovky"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Nerušit"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Režimy priority"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nejsou dostupná žádná spárovaná zařízení"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Klepnutím zařízení připojíte nebo odpojíte"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Používat Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Připojeno"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Sdílení zvuku"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Klepnutím přepnete nebo nasdílíte zvuk"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojit"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovat"</string>
@@ -389,7 +392,7 @@
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
<string name="performance" msgid="6552785217174378320">"Výkon"</string>
<string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string>
- <string name="thermal" msgid="6758074791325414831">"Termovize"</string>
+ <string name="thermal" msgid="6758074791325414831">"Teplota"</string>
<string name="custom" msgid="3337456985275158299">"Vlastní"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Vlastní nastavení trasování"</string>
<string name="restore_default" msgid="5259420807486239755">"Obnovit výchozí"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otevřít nastavení"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Další zařízení"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Přepnout přehled"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Režimy priority"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavení"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Zapnuto"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Služba, která tuto funkci poskytuje, bude mít při nahrávání nebo odesílání přístup ke všem informacím, které jsou viditelné na obrazovce nebo které jsou přehrávány ze zařízení. Týká se to i hesel, údajů o platbě, fotek, zpráv a přehrávaných zvuků."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Sdílení nebo nahrání aplikace"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Sdílet obrazovku s aplikací <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Sdílet jednu aplikaci"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Sdílet celou obrazovku"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Sdílet jednu aplikaci"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Sdílet celou obrazovku"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Při sdílení celé obrazovky vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Při sdílení aplikace vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se ve sdílené aplikaci nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Sdílet obrazovku"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobré připojení"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, připojení je k dispozici"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS přes satelit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Tísňová volání nebo SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Pracovní profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zábava, která není pro každého"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Nástroj na ladění uživatelského rozhraní systému vám nabízí další způsoby, jak si vyladit a přizpůsobit uživatelské rozhraní Android. Tyto experimentální funkce mohou v dalších verzích chybět, nefungovat nebo být změněny. Postupujte proto prosím opatrně."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto zpět"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto domů"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Akční klávesa"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 9b72478..974083e 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og andre åbne apps har registreret dette screenshot."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Føj til note"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Inkluder link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Skærmoptagelse"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skærmoptagelse"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Konstant notifikation om skærmoptagelse"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Pauseskærm"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Forstyr ikke"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Tilstande med prioritet"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Der er ingen tilgængelige parrede enheder"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tryk for at oprette eller afbryde forbindelse til en enhed"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Brug Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Der er oprettet forbindelse"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Lyddeling"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tryk for at skifte eller dele lyd"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"afbryd forbindelse"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivér"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Åbn Indstillinger"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Anden enhed"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå Oversigt til/fra"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Tilstande med prioritet"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Udfør"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Indstillinger"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Til"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Tjenesten, der tilbyder denne funktion, får adgang til alle de oplysninger, der er synlige på din skærm, eller som afspilles på din enhed, når du optager eller caster. Dette omfatter oplysninger som f.eks. adgangskoder, betalingsoplysninger, billeder, beskeder og afspillet lyd."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Del eller optag en app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vil du dele din skærm med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Del én app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Del hele skærmen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Del én app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Del hele skærmen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Når du deler hele skærmen, er alt på din skærm synligt for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Vær derfor forsigtig med f.eks. adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Når du deler en app, er alt, der vises eller afspilles i den pågældende app, synligt for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Vær derfor forsigtig med f.eks. adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Del skærm"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit – god forbindelse"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit – forbindelsen er tilgængelig"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-meldinger via satellit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Nødopkald eller SOS-meldinger via satellit"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Arbejdsprofil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Sjovt for nogle, men ikke for alle"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner giver dig flere muligheder for at justere og tilpasse Android-brugerfladen. Disse eksperimentelle funktioner kan ændres, gå i stykker eller forsvinde i fremtidige udgivelser. Vær forsigtig, hvis du fortsætter."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Bevægelse for at gå tilbage"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Bevægelse for at gå til startskærm"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Handlingstast"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 7bfdf67..11dd46e 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> und andere geöffnete Apps haben diesen Screenshot erkannt."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Zu Notiz hinzufügen"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Link einschließen"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Bildschirmaufzeichnung"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Bildschirmaufzeichnung…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Fortlaufende Benachrichtigung für eine Bildschirmaufzeichnung"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Bildschirmschoner"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Bitte nicht stören"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritätsmodi"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Keine gekoppelten Geräte verfügbar"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Zum Verbinden oder Trennen eines Geräts tippen"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth verwenden"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbunden"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audiofreigabe"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Zum Wechseln oder Teilen des Audiostreams tippen"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Verknüpfung aufheben"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivieren"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Einstellungen öffnen"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Sonstiges Gerät"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Übersicht ein-/ausblenden"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritätsmodi"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fertig"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Einstellungen"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"An"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"App teilen oder aufnehmen"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Bildschirm mit <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> teilen?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Eine App streamen"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Gesamten Bildschirm teilen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Eine App teilen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Gesamten Bildschirm teilen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Wenn du den gesamten Bildschirm teilst, ist für <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alles auf dem Bildschirm sichtbar. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Wenn du eine App streamst, ist für <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alles sichtbar, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bildschirm teilen"</string>
@@ -629,7 +637,7 @@
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Einstellungen"</string>
<string name="volume_panel_captioning_title" msgid="5984936949147684357">"Automatische Untertitel"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Auf verträglichere Lautstärke eingestellt"</string>
- <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Die Kopfhörerlautstärke war länger als empfohlen hoch eingestellt"</string>
+ <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Die Kopfhörer sind schon länger als empfohlen auf hohe Lautstärke eingestellt"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Die Kopfhörerlautstärke hat für diese Woche das Sicherheitslimit überschritten"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Weiterhören"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Leiser stellen"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit, Verbindung gut"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit, Verbindung verfügbar"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Notruf über Satellit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Notrufe oder SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Arbeitsprofil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Für einige ein Vergnügen, aber nicht für alle"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Mit System UI Tuner erhältst du zusätzliche Möglichkeiten, die Android-Benutzeroberfläche anzupassen. Achtung: Diese Testfunktionen können sich ändern, abstürzen oder in zukünftigen Versionen verschwinden."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Touch-Geste „Zurück“"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Touch-Geste „Startbildschirm“"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Aktionstaste"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index ed17959..433137c 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Η εφαρμογή <xliff:g id="APPNAME">%1$s</xliff:g> και άλλες ανοικτές εφαρμογές εντόπισαν το στιγμιότυπο οθόνης."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Προσθήκη σε σημείωση"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Συμπερίληψη συνδέσμου"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Εγγραφή οθόνης"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Επεξεργασία εγγραφής οθόνης"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ειδοποίηση σε εξέλιξη για μια περίοδο λειτουργίας εγγραφής οθόνης"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Προφύλαξη οθόνης"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Μην ενοχλείτε"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Λειτουργίες προτεραιότητας"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Δεν υπάρχουν διαθέσιμες συσκευές σε σύζευξη"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Πατήστε για σύνδεση ή αποσύνδεση μιας συσκευής"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Χρήση Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Συνδέθηκε"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Κοινή χρήση ήχου"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Πατήστε για εναλλαγή ή κοινή χρήση ήχου"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Αποθηκεύτηκε"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"αποσύνδεση"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ενεργοποίηση"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Άνοιγμα Ρυθμίσεων"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Άλλη συσκευή"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Εναλλαγή επισκόπησης"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Λειτουργίες προτεραιότητας"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Τέλος"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ρυθμίσεις"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Ενεργό"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Η υπηρεσία που παρέχει αυτήν τη λειτουργία θα έχει πρόσβαση σε όλες τις πληροφορίες που εμφανίζονται στην οθόνη σας ή που αναπαράγονται από τη συσκευή σας κατά την εγγραφή ή τη μετάδοση. Αυτό περιλαμβάνει πληροφορίες όπως κωδικούς πρόσβασης, στοιχεία πληρωμής, φωτογραφίες, μηνύματα και ήχο που αναπαράγετε."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Κοινή χρήση ή εγγραφή εφαρμογής"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Κοινή χρήση της οθόνης με την εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>;"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Κοινή χρήση μίας εφαρμογής"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Κοινή χρήση ολόκληρης της οθόνης"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Κοινή χρήση μίας εφαρμογής"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Κοινή χρήση ολόκληρης της οθόνης"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Όταν μοιράζεστε ολόκληρη την οθόνη, οτιδήποτε εμφανίζεται στην οθόνη σας είναι ορατό στην εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Όταν μοιράζεστε μια εφαρμογή, οτιδήποτε εμφανίζεται ή αναπαράγεται σε αυτή την εφαρμογή, είναι ορατό στην εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Κοινή χρήση οθόνης"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Δορυφορική, καλή σύνδεση"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Δορυφορική, διαθέσιμη σύνδεση"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Δορυφορικό SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Κλήσεις έκτακτης ανάγκης ή SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Προφίλ εργασίας"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Διασκέδαση για ορισμένους, αλλά όχι για όλους"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Το System UI Tuner σάς προσφέρει επιπλέον τρόπους για να τροποποιήσετε και να προσαρμόσετε τη διεπαφή χρήστη Android. Αυτές οι πειραματικές λειτουργίες ενδέχεται να τροποποιηθούν, να παρουσιάσουν σφάλματα ή να καταργηθούν σε μελλοντικές εκδόσεις. Συνεχίστε με προσοχή."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Κίνηση επιστροφής"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Κίνηση μετάβασης στην αρχική οθόνη"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Πλήκτρο ενέργειας"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 0565be8..cb9dfe5 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Screen recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio sharing"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Share one app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 17642f7..3d4c31f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -291,7 +292,7 @@
<string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+ <string name="quick_settings_modes_label" msgid="879156359479504244">"Modes"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -300,6 +301,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio Sharing"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -431,7 +433,7 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open Settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+ <string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
@@ -532,8 +534,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Share one app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you’re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you’re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
@@ -1385,6 +1391,12 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+ <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
+ <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
+ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts, and more"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 0565be8..cb9dfe5 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Screen recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio sharing"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Share one app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 0565be8..cb9dfe5 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Screen recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio sharing"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Share one app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index d31d328..eb94d59 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -291,7 +292,7 @@
<string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+ <string name="quick_settings_modes_label" msgid="879156359479504244">"Modes"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -300,6 +301,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio Sharing"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -431,7 +433,7 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open Settings"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+ <string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
@@ -532,8 +534,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Share one app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Share entire screen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you’re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you’re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
@@ -1385,6 +1391,12 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+ <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
+ <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
+ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts, and more"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 53c38e2..2af8bd1 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -104,13 +104,14 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> y otras apps en ejecución detectaron que tomaste una captura de pantalla."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Agregar a la nota"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Incluir vínculo"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Grabadora de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación constante para una sesión de grabación de pantalla"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Quieres grabar la pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabes toda la pantalla, se registrará todo lo que se muestre en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabes toda la pantalla, se grabará todo lo que se muestre en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabes una app, se registrará todo lo que se muestre o reproduzca en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Elige una app para grabar"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Protector pantalla"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"No interrumpir"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritarios"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hay dispositivos sincronizados disponibles"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Presiona para conectar o desconectar un dispositivo"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Uso compartido de audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Presiona para cambiar o compartir el audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Configuración"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ocultar o mostrar Recientes"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritarios"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Listo"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"El servicio que brinda esta función tendrá acceso a toda la información que sea visible en la pantalla o que reproduzcas en el dispositivo durante una grabación o transmisión. Se incluyen contraseñas, detalles de pagos, fotos, mensajes y audio que reproduzcas."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Comparte o graba una app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"¿Quieres compartir pantalla con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Compartir una app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Compartir pantalla completa"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartir una app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir pantalla completa"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Cuando compartes la pantalla completa, todo lo que se muestre es visible en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Cuando compartes una app, todo lo que se muestre o reproduzca en ella será visible en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, buena conexión"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Llamadas de emergencia o SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunas personas"</string>
<string name="tuner_warning" msgid="1861736288458481650">"El sintonizador de IU del sistema te brinda más formas para editar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, dejar de funcionar o no incluirse en futuras versiones. Procede con precaución."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto atrás"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto para ir a la pantalla principal"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de acción"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 0bebccf..2e823ed 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> y otras aplicaciones abiertas han detectado esta captura de pantalla."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Añadir a nota"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Incluir enlace"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Grabación de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación continua de una sesión de grabación de la pantalla"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Salvapantallas"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"No molestar"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritarios"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hay dispositivos vinculados disponibles"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca para conectar o desconectar un dispositivo"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartir audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toca para cambiar o compartir el audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
@@ -390,7 +393,7 @@
<string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
<string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
<string name="thermal" msgid="6758074791325414831">"Temperatura"</string>
- <string name="custom" msgid="3337456985275158299">"Otro"</string>
+ <string name="custom" msgid="3337456985275158299">"Config. personalizada"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Ajustes de traza personalizados"</string>
<string name="restore_default" msgid="5259420807486239755">"Restaurar ajustes predeterminados"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Ajustes"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Mostrar u ocultar aplicaciones recientes"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritarios"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hecho"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ajustes"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"El servicio que ofrece esta función tendrá acceso a toda la información que se muestre en la pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluidos contraseñas, detalles de pagos, fotos, mensajes y audio que reproduzcas."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Compartir o grabar una aplicación"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"¿Compartir tu pantalla con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Compartir una aplicación"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Compartir toda la pantalla"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartir una aplicación"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir toda la pantalla"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Cuando compartes toda tu pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede ver todo lo que hay en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Cuando compartes una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede ver todo lo que se muestra o reproduce en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, buena conexión"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Llamadas de emergencia o SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"El configurador de UI del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto para volver"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto para ir al inicio"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de acción"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index b2ffa80..09f461f 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ja muud avatud rakendused tuvastasid selle ekraanipildi."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Lisa märkmesse"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Kaasa link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Ekraanisalvesti"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekraanisalvestuse töötlemine"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pooleli märguanne ekraanikuva salvestamise seansi puhul"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Ekraanisäästja"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Mitte segada"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteetsed režiimid"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ühtegi seotud seadet pole saadaval"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Puudutage seadme ühendamiseks või ühenduse katkestamiseks"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Kasuta Bluetoothi"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ühendatud"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Heli jagamine"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Puudutage helile lülitumiseks või selle jagamiseks"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ava menüü Seaded"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Muu seade"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteetsed režiimid"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Seaded"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Sees"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Seda funktsiooni pakkuv teenus saab juurdepääsu kogu teabele, mis on teie ekraanikuval nähtav või mida seadmes salvestamise või ülekande ajal esitatakse. See hõlmab teavet, nagu paroolid, maksete üksikasjad, fotod, sõnumid ja esitatav heli."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Rakenduse jagamine või salvestamine"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Kas jagada teie ekraani rakendusega <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Jaga üht rakendust"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Jaga kogu ekraani"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Jaga ühte rakendust"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Jaga kogu ekraani"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kogu ekraanikuva jagamisel on kogu sellel kuvatav sisu nähtav rakendusele <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Rakenduse jagamisel on kogu rakenduses kuvatav või esitatav sisu nähtav rakendusele <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Jaga ekraani"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliit, hea ühendus"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliit, ühendus on saadaval"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelliit-SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hädaabikõned või SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Tööprofiil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Kõik ei pruugi sellest rõõmu tunda"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Süsteemi kasutajaliidese tuuner pakub täiendavaid võimalusi Androidi kasutajaliidese muutmiseks ja kohandamiseks. Need katselised funktsioonid võivad muutuda, rikki minna või tulevastest versioonidest kaduda. Olge jätkamisel ettevaatlik."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tagasiliikumisliigutus"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Avakuvale liikumise liigutus"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Toiminguklahv"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index d8cff19..329b6e2 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioak eta irekitako beste aplikazio batzuek pantaila-argazkia hauteman dute."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Gehitu oharrean"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Sartu esteka"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Pantaila-grabagailua"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pantaila-grabaketa prozesatzen"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Pantaila-babeslea"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ez molestatzeko modua"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Lehentasunezko moduak"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetootha"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ez dago parekatutako gailurik erabilgarri"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Sakatu hau gailu bat konektatu edo deskonektatzeko"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Erabili Bluetootha"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Konektatuta"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audioa partekatzea"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Audioa aldatu edo partekatzeko, sakatu hau"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deskonektatu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktibatu"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ireki Ezarpenak"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Beste gailu bat"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aldatu ikuspegi orokorra"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Lehentasunezko moduak"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Eginda"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ezarpenak"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Aktibatuta"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Zerbait grabatzen edo igortzen duzunean, pantailan ikusgai dagoen edo gailuak erreproduzitzen duen informazio guztia erabili ahalko du funtzio hori eskaintzen duen zerbitzuak. Pasahitzak, ordainketen xehetasunak, argazkiak, mezuak eta erreproduzitzen dituzun audioak sartzen dira informazio horretan."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Partekatu edo grabatu aplikazio bat"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioarekin pantaila partekatu nahi duzu?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Partekatu aplikazio bat"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Partekatu pantaila osoa"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Aplikazio bat partekatu"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Pantaila osoa partekatu"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pantaila osoa partekatzen ari zarenean, pantailan duzun guztia ikus dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Aplikazio bat partekatzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia ikusi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partekatu pantaila"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelitea, konexio ona"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelitea, konexioa erabilgarri"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelite bidezko SOS komunikazioa"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Larrialdi-deiak edo SOS komunikazioa"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Laneko profila"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Dibertsioa batzuentzat, baina ez guztientzat"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sistemaren erabiltzaile-interfazearen konfiguratzaileak Android erabiltzaile-interfazea moldatzeko eta pertsonalizatzeko modu gehiago eskaintzen dizkizu. Baliteke eginbide esperimental horiek hurrengo kaleratzeetan aldatuta, etenda edo desagertuta egotea. Kontuz erabili."</string>
@@ -1195,7 +1202,7 @@
<string name="select_conversation_text" msgid="3376048251434956013">"Sakatu elkarrizketa bat orri nagusian gehitzeko"</string>
<string name="no_conversations_text" msgid="5354115541282395015">"Azkenaldiko elkarrizketak agertuko dira hemen"</string>
<string name="priority_conversations" msgid="3967482288896653039">"Lehentasunezko elkarrizketak"</string>
- <string name="recent_conversations" msgid="8531874684782574622">"Azken elkarrizketak"</string>
+ <string name="recent_conversations" msgid="8531874684782574622">"Azkenaldiko elkarrizketak"</string>
<string name="days_timestamp" msgid="5821854736213214331">"Duela <xliff:g id="DURATION">%1$s</xliff:g> egun"</string>
<string name="one_week_timestamp" msgid="4925600765473875590">"Duela astebete"</string>
<string name="two_weeks_timestamp" msgid="9111801081871962155">"Duela bi aste"</string>
@@ -1219,7 +1226,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"Laster agertuko da edukia"</string>
<string name="missed_call" msgid="4228016077700161689">"Dei galdua"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"Ikusi azken mezuak, dei galduak eta egoerei buruzko informazio eguneratua"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"Ikusi azkenaldiko mezuak, dei galduak eta egoerei buruzko informazio eguneratua"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Elkarrizketa"</string>
<string name="paused_by_dnd" msgid="7856941866433556428">"Ez molestatzeko moduak pausatu du"</string>
<string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzaileak mezu bat bidali du: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Atzera egiteko keinua"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Orri nagusira joateko keinua"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Ekintza-tekla"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 5b17c44..f845027 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> و سایر برنامههای باز این نماگرفت را تشخیص دادند."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"افزودن به یادداشت"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"اضافه کردن پیوند"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"ضبطکن صفحهنمایش"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"درحال پردازش ضبط صفحهنمایش"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"اعلان درحال انجام برای جلسه ضبط صفحهنمایش"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"محافظ صفحه"</string>
<string name="ethernet_label" msgid="2203544727007463351">"اترنت"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"مزاحم نشوید"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"حالتهای اولویتدار"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوتوث"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"هیچ دستگاه مرتبط شدهای موجود نیست"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"برای اتصال یا قطع اتصال دستگاه، تکضرب بزنید"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"استفاده از بلوتوث"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متصل"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"اشتراک صدا"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"برای فعال کردن و همرسانی صدا، تکضرب بزنید"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ذخیرهشده"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"قطع اتصال"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کردن"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"باز کردن تنظیمات"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"دستگاه دیگر"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"حالتهای اولویتدار"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"تمام"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"تنظیمات"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"روشن"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"سرویس ارائهدهنده این عملکرد به همه اطلاعاتی که روی صفحهنمایش قابلمشاهد است و هنگام ضبط کردن یا پخش محتوا از دستگاهتان پخش میشود دسترسی خواهد داشت. این شامل اطلاعاتی مانند گذرواژهها، جزئیات پرداخت، عکسها، پیامها، و صداهایی که پخش میکنید میشود."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"همرسانی یا ضبط برنامه"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"صفحهنمایش با <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> همرسانی شود؟"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"همرسانی یک برنامه"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"همرسانی کل صفحهنمایش"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"همرسانی کردن یک برنامه"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"همرسانی کردن کل صفحهنمایش"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"وقتی کل صفحهنمایش را همرسانی میکنید، هر چیزی که روی صفحهنمایش شما وجود داشته باشد برای <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> قابلمشاهده خواهد بود. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"وقتی برنامهای را همرسانی میکنید، هر چیزی که در آن برنامه نمایش داده شود یا پخش شود برای <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> قابلمشاهده خواهد بود. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"همرسانی صفحهنمایش"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ماهواره، اتصال خوب است"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ماهواره، اتصال دردسترس است"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"درخواست کمک ماهوارهای"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"تماس اضطراری یا درخواست کمک اضطراری"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"نمایه کاری"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"برای بعضی افراد سرگرمکننده است اما نه برای همه"</string>
<string name="tuner_warning" msgid="1861736288458481650">"«تنظیمکننده واسط کاربری سیستم» روشهای بیشتری برای تنظیم دقیق و سفارشی کردن واسط کاربری Android در اختیار شما قرار میدهد. ممکن است این ویژگیهای آزمایشی تغییر کنند، خراب شوند یا در نسخههای آینده جود نداشته باشند. با احتیاط ادامه دهید."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"اشاره برگشت"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"اشاره صفحه اصلی"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"دکمه کنش"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 9244bec..b9afcd5 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ja jotkin muut sovellukset havaitsivat tämän kuvakaappauksen."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Lisää muistiinpanoon"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Lisää linkki"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Näytön tallentaja"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Näytön tallennusta käsitellään"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Näytönsäästäjä"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Älä häiritse"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteettitilat"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Laitepareja ei ole käytettävissä"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Muodosta yhteys laitteeseen tai katkaise yhteys napauttamalla"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Käytä Bluetoothia"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Yhdistetty"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audionjako"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Vaihda tai jaa audiota napauttamalla"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkaise yhteys"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivoi"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Avaa Asetukset"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Muu laite"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Näytä/piilota viimeisimmät"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteettitilat"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Asetukset"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Päällä"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Ominaisuuden tarjoavalla palvelulla on pääsy kaikkiin näytölläsi näkyviin tietoihin ja tietoihin laitteesi toistamasta sisällöstä tallennuksen tai striimauksen aikana. Näitä tietoja ovat esimerkiksi salasanat, maksutiedot, kuvat, viestit ja toistettava audiosisältö."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Jaa sovellus tai tallenna sen sisältöä"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Saako <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> nähdä näyttösi?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Jaa yksi sovellus"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Jaa koko näyttö"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Jaa yksi sovellus"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Jaa koko näyttö"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kun jaat koko näytön, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> näkee kaiken sen sisälllön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kun jaat sovelluksen, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> näkee kaiken sovelluksessa näkyvän tai toistetun sisällön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Jaa näyttö"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliitti, hyvä yhteys"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliitti, yhteys saatavilla"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hätäpuhelut tai Satellite SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Työprofiili"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Ei sovellu kaikkien käyttöön"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner antaa lisämahdollisuuksia Android-käyttöliittymän muokkaamiseen. Nämä kokeelliset ominaisuudet voivat muuttua, lakata toimimasta tai kadota milloin tahansa. Jatka omalla vastuullasi."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Takaisin-ele"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Etusivu-ele"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Toimintonäppäin"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 41305af..07e131e 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> et d\'autres applis ouvertes ont détecté cette capture d\'écran."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ajouter à une note"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Inclure le lien"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Enregistreur d\'écran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Trait. de l\'enregist. d\'écran…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement d\'écran"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Écran de veille"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne pas déranger"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaires"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Aucun des appareils associés n\'est disponible"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Touchez pour connecter ou déconnecter un appareil"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Partage audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Touchez pour passer d\'un appareil à l\'autre ou pour partager le contenu audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Déconnecter"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"Activer"</string>
@@ -307,7 +310,7 @@
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les fonctionnalités comme Partage rapide et Localiser mon appareil utilisent le Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth s\'activera demain matin"</string>
<string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partager l\'audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Partage de l\'audio en cours…"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Partage de l\'audio en cours"</string>
<string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"entrer les paramètres de partage audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Basculer l\'aperçu"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaires"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Paramètres"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Le service offrant cette fonction aura accès à toute l\'information qui est visible sur votre écran ou lu sur votre appareil pendant que vous enregistrez ou diffusez. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Partager ou enregistrer une appli"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Partager votre écran avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Partager une appli"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Partager l\'intégralité de l\'écran"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Partager une appli"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partager l\'intégralité de l\'écran"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Lorsque vous partagez l\'intégralité de votre écran, tout ce qui s\'y trouve est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Lorsque vous partagez une appli, tout ce qui s\'y affiche ou s\'y joue est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partager l\'écran"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Bonne connexion satellite"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Connexion satellite accessible"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS par satellite"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Appels d\'urgence ou SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vous propose de nouvelles manières d\'adapter et de personnaliser l\'interface utilisateur d\'Android. Ces fonctionnalités expérimentales peuvent être modifiées, cesser de fonctionner ou disparaître dans les versions futures. À utiliser avec prudence."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geste de retour"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Geste d\'accès à l\'écran d\'accueil"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Touche d\'action"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 4df98cf..9d3bbd7 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -104,13 +104,14 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> et d\'autres applis ouvertes ont détecté cette capture d\'écran."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ajouter à la note"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Inclure le lien"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Enregistreur d\'écran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Enregistrement de l\'écran…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement de l\'écran"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer l\'écran ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer tout l\'écran"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez tout votre écran, tout ce qui s\'affiche sur celui-ci est enregistré. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'y affiche est enregistré. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos, et les contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans celle-ci est enregistré. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Choisir l\'appli à enregistrer"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Économiseur d\'écran"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne pas déranger"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaires"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Aucun appareil associé disponible."</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Appuyez pour connecter ou déconnecter un appareil"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Partage audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Appuyez pour activer ou partager l\'audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"dissocier"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activer"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'écran Récents"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaires"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Paramètres"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Le service qui fournit cette fonction aura accès à toutes les infos visibles sur votre écran ou lues depuis votre appareil pendant un enregistrement ou une diffusion de contenu. Il peut s\'agir de mots de passe, détails de mode de paiement, photos, messages ou encore contenus audio lus."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Partager ou enregistrer une appli"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Partager votre écran avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Partager une appli"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Partager tout l\'écran"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Partager une appli"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partager tout l\'écran"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Lorsque vous partagez tout votre écran, l\'ensemble de son contenu est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Lorsque vous partagez une appli, tout ce qui est affiché ou lu dans celle-ci est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partager l\'écran"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Bonne connexion satellite"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Connexion satellite disponible"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS par satellite"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Appels d\'urgence ou SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vous propose de nouvelles manières d\'adapter et de personnaliser l\'interface utilisateur Android. Ces fonctionnalités expérimentales peuvent être modifiées, cesser de fonctionner ou disparaître dans les versions futures. À utiliser avec prudence."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geste Retour"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Geste Accueil"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Touche d\'action"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 1fe1242..6281570 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outras aplicacións abertas detectaron esta captura de pantalla."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Engadir a unha nota"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Incluír ligazón"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Gravadora da pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando gravación pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación de actividade en curso sobre unha sesión de gravación de pantalla"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Protector pantalla"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Non molestar"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos de prioridade"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Non hai dispositivos vinculados dispoñibles"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca para conectar ou desconectar un dispositivo"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Estableceuse a conexión"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio compartido"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toca para cambiar ou compartir o audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Configuración"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activar/desactivar Visión xeral"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos de prioridade"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Feito"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"O servizo que proporciona esta función terá acceso a toda a información visible na pantalla ou reproducida desde o teu dispositivo mentres graves ou emitas contido. Isto inclúe datos como contrasinais, detalles de pago, fotos, mensaxes e o audio que reproduzas."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Compartir ou gravar unha aplicación"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Queres compartir a pantalla con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Compartir unha aplicación"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Compartir toda a pantalla"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartir unha aplicación"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir toda a pantalla"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Se compartes toda a pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> poderá ver todo o contido que apareza nela. Ten coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Se compartes toda a pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> poderá ver todo o contido que apareza ou se reproduza nesa aplicación. Ten coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, boa conexión"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión dispoñible"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chamadas de emerxencia ou SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de traballo"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversión só para algúns"</string>
<string name="tuner_warning" msgid="1861736288458481650">"O configurador da IU do sistema ofréceche formas adicionais de modificar e personalizar a interface de usuario de Android. Estas funcións experimentais poden cambiar, interromperse ou desaparecer en futuras versións. Continúa con precaución."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Xesto para volver"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Xesto para ir ao inicio"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de acción"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 7ecc057..2be88f2 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> અને કામ કરતી અન્ય ઍપ દ્વારા આ સ્ક્રીનશૉટ લેવાયાની ભાળ મેળવવામાં આવી."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"નોંધમાં ઉમેરો"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"લિંક શામેલ કરો"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"સ્ક્રીન રેકોર્ડર"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"સ્ક્રીન રેકૉર્ડિંગ ચાલુ છે"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"સ્ક્રીન રેકોર્ડિંગ સત્ર માટે ચાલુ નોટિફિકેશન"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"સ્ક્રીન સેવર"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ઇથરનેટ"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ખલેલ પાડશો નહીં"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"પ્રાધાન્યતાના મોડ"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"બ્લૂટૂથ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"કોઈ જોડી કરેલ ઉપકરણો ઉપલબ્ધ નથી"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"કોઈ ડિવાઇસ કનેક્ટ કરવા કે ડિસ્કનેક્ટ કરવા માટે ટૅપ કરો"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"બ્લૂટૂથનો ઉપયોગ કરો"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"કનેક્ટેડ છે"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ઑડિયો શેરિંગ"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ઑડિયો પર સ્વિચ કરવા કે તેને શેર કરવા માટે બે વાર ટૅપ કરો"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"સાચવેલું"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ડિસ્કનેક્ટ કરો"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"સક્રિય કરો"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"સેટિંગ ખોલો"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"અન્ય ડિવાઇસ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ઝલકને ટૉગલ કરો"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"પ્રાધાન્યતાના મોડ"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"થઈ ગયું"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"સેટિંગ"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ચાલુ"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"રેકોર્ડ અથવા કાસ્ટ કરતી વખતે, આ સુવિધા આપતી સેવાને તમારી સ્ક્રીન પર દેખાતી હોય અથવા તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી માહિતીનો ઍક્સેસ હશે. જેમાં પાસવર્ડ, ચુકવણીની વિગતો, ફોટા, મેસેજ અને તમે વગાડો છો તે ઑડિયો જેવી માહિતીનો સમાવેશ થાય છે."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"કોઈ ઍપ શેર કરો અથવા રેકોર્ડ કરો"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> સાથે તમારી સ્ક્રીન શેર કરીએ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"એક ઍપ શેર કરો"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"સંપૂર્ણ સ્ક્રીન શેર કરો"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"કોઈ ઍપ શેર કરો"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"સંપૂર્ણ સ્ક્રીન શેર કરો"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"જ્યારે તમે તમારી સંપૂર્ણ સ્ક્રીનને શેર કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પરની કોઈપણ વસ્તુ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"જ્યારે તમે કોઈ ઍપને શેર કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"સ્ક્રીન શેર કરો"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"સૅટલાઇટ, સારું કનેક્શન"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"સૅટલાઇટ, કનેક્શન ઉપલબ્ધ છે"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"ઇમર્જન્સી સૅટલાઇટ સહાય"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ઇમર્જન્સી કૉલ અથવા SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ઑફિસની પ્રોફાઇલ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"કેટલાક માટે મજા પરંતુ બધા માટે નહીં"</string>
<string name="tuner_warning" msgid="1861736288458481650">"સિસ્ટમ UI ટ્યૂનર તમને Android વપરાશકર્તા ઇન્ટરફેસને ટ્વીક અને કસ્ટમાઇઝ કરવાની વધારાની રીતો આપે છે. ભાવિ રીલિઝેસમાં આ પ્રાયોગિક સુવિધાઓ બદલાઈ, ભંગ અથવા અદૃશ્ય થઈ શકે છે. સાવધાની સાથે આગળ વધો."</string>
@@ -1222,7 +1229,7 @@
<string name="people_tile_description" msgid="8154966188085545556">"તાજેતરના મેસેજ, ચૂકી ગયેલા કૉલ અને સ્ટેટસ અપડેટ જુઓ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"વાતચીત"</string>
<string name="paused_by_dnd" msgid="7856941866433556428">"\'ખલેલ પાડશો નહીં\'ની સુવિધા દ્વારા થોભાવેલું"</string>
- <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ સંદેશ મોકલવામાં આવ્યો: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
+ <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ મેસેજ મોકલવામાં આવ્યો: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ છબી મોકલવામાં આવી"</string>
<string name="new_status_content_description" msgid="6046637888641308327">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા નવી સ્ટેટસ અપડેટ પોસ્ટ કરવામાં આવી: <xliff:g id="STATUS">%2$s</xliff:g>"</string>
<string name="person_available" msgid="2318599327472755472">"ઉપલબ્ધ છે"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"પાછળ જવાનો સંકેત"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"હોમ સ્ક્રીન પર જવાનો સંકેત"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ઍક્શન કી"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index c6a5e57..7c267bf 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -104,13 +104,14 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> और खुले हुए अन्य ऐप्लिकेशन को इस स्क्रीनशॉट का पता चला है."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"नोट में जोड़ें"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"लिंक जोड़ें"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"स्क्रीन रिकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रिकॉर्डिंग को प्रोसेस किया जा रहा है"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रिकॉर्ड सेशन के लिए जारी सूचना"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"क्या आपको स्क्रीन रिकॉर्ड करनी है?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक ऐप्लिकेशन की रिकॉर्डिंग करें"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरी स्क्रीन रिकॉर्ड करें"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"पूरी स्क्रीन रिकॉर्ड करते समय, स्क्रीन पर दिखने वाली हर चीज़ रिकॉर्ड की जाती है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"पूरी स्क्रीन रिकॉर्ड करते समय, स्क्रीन पर दिखने वाली हर चीज़ रिकॉर्ड की जाती है. इसलिए पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, डिवाइस पर चल रहे ऑडियो और वीडियो, और फ़ोटो जैसी चीज़ों को लेकर सावधानी बरतें."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"किसी ऐप्लिकेशन को रिकॉर्ड करने के दौरान, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर भी रिकॉर्ड होता है. इसलिए, रिकॉर्ड करते समय पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रिकॉर्ड करें"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"रिकॉर्ड करने के लिए ऐप्लिकेशन चुनें"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"स्क्रीन सेवर"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ईथरनेट"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"परेशान न करें"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"अहम मोड"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ब्लूटूथ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"कोई भी युग्मित डिवाइस उपलब्ध नहीं"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"किसी डिवाइस को कनेक्ट या डिसकनेक्ट करने के लिए टैप करें"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ इस्तेमाल करें"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट है"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ऑडियो शेयर करने की सुविधा"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ऑडियो को स्विच या शेयर करने के लिए टैप करें"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव किया गया"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिसकनेक्ट करें"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"चालू करें"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिंग खोलें"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"अन्य डिवाइस"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"खास जानकारी टॉगल करें"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"अहम मोड"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"हो गया"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिंग"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"चालू है"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"रिकॉर्ड या कास्ट करते समय, इस सुविधा को उपलब्ध कराने वाली सेवा के पास आपकी स्क्रीन पर दिख रही जानकारी या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. जैसे, पासवर्ड, पेमेंट के तरीके की जानकारी, फ़ोटो, मैसेज, और डिवाइस पर चल रहा ऑडियो."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ऐप्लिकेशन शेयर करें या उसकी रिकॉर्डिंग करें"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"क्या आपको <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर अपनी स्क्रीन शेयर करनी है?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"एक ऐप्लिकेशन शेयर करें"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"पूरी स्क्रीन शेयर करें"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"एक ऐप्लिकेशन शेयर करें"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"पूरी स्क्रीन शेयर करें"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"जब पूरी स्क्रीन शेयर की जाती है, तो आपकी स्क्रीन पर दिख रहा पूरा कॉन्टेंट <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"जब कोई ऐप्लिकेशन शेयर किया जाता है, तो उस ऐप्लिकेशन में दिख रहा या चलाया जा रहा पूरा कॉन्टेंट <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रीन शेयर करें"</string>
@@ -628,8 +636,8 @@
<string name="sound_settings" msgid="8874581353127418308">"आवाज़ और वाइब्रेशन"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"सेटिंग"</string>
<string name="volume_panel_captioning_title" msgid="5984936949147684357">"लाइव कैप्शन"</string>
- <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"आवाज़ को कम करके, सुरक्षित लेवल पर सेट कर दिया गया है"</string>
- <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफ़ोन की आवाज़ सुझाए गए समय से देर तक ज़्यादा रही"</string>
+ <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"आवाज़ को कम करके, सुरक्षित लेवल पर सेट किया गया"</string>
+ <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफ़ोन की आवाज़ सुझाए गए समय के बाद भी ज़्यादा रही"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"इस हफ़्ते के लिए हेडफ़ोन की आवाज़, सुझाई गई सीमा से ज़्यादा हो गई है"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"सुनना जारी रखें"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"आवाज़ कम करें"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"सैटलाइट कनेक्शन अच्छा है"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"सैटलाइट कनेक्शन उपलब्ध है"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"सैटलाइट एसओएस"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"आपातकालीन कॉल या एसओएस"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"वर्क प्रोफ़ाइल"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"कुछ के लिए मज़ेदार लेकिन सबके लिए नहीं"</string>
<string name="tuner_warning" msgid="1861736288458481650">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर, आपको Android यूज़र इंटरफ़ेस में सुधार लाने और उसे अपनी पसंद के हिसाब से बदलने के कुछ और तरीके देता है. प्रयोग के तौर पर इस्तेमाल हो रहीं ये सुविधाएं आगे चल कर रिलीज़ की जा सकती हैं, रोकी जा सकती हैं या दिखाई देना बंद हो सकती हैं. सावधानी से आगे बढ़ें."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"पीछे जाने का जेस्चर"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम स्क्रीन पर जाने का जेस्चर"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ऐक्शन बटन"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 2edf138..bdf6e13 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije otkrile su ovu snimku zaslona."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj bilješci"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Uključi vezu"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Snimač zaslona"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrada snimanja zaslona"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Tekuća obavijest za sesiju snimanja zaslona"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Čuvar zaslona"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne uznemiravaj"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni načini"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Upareni uređaji nisu dostupni"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da biste povezali uređaj ili prekinuli vezu s njim"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Zajedničko slušanje"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dodirnite za prebacivanje ili dijeljenje zvuka"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekini vezu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviraj"</string>
@@ -389,7 +392,7 @@
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string>
<string name="performance" msgid="6552785217174378320">"Izvedba"</string>
<string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string>
- <string name="thermal" msgid="6758074791325414831">"Termalno"</string>
+ <string name="thermal" msgid="6758074791325414831">"Pregrijavanje"</string>
<string name="custom" msgid="3337456985275158299">"Prilagođeno"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Postavke prilagođenog praćenja"</string>
<string name="restore_default" msgid="5259420807486239755">"Vrati na zadano"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori postavke"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ostali uređaji"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključivanje/isključivanje pregleda"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni načini"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Postavke"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Usluga koja pruža ovu funkciju imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Dijeljenje ili snimanje pomoću aplikacije"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Želite li dijeliti zaslon s aplikacijom <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Dijeljenje jedne aplikacije"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Dijeljenje cijelog zaslona"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Dijeljenje jedne aplikacije"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dijeljenje cijelog zaslona"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada dijelite cijeli zaslon, sve na zaslonu bit će vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada dijelite aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji bit će vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dijeljenje zaslona"</string>
@@ -629,7 +637,7 @@
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Postavke"</string>
<string name="volume_panel_captioning_title" msgid="5984936949147684357">"Automatski titlovi"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Glasnoća je stišana na sigurniju razinu"</string>
- <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Glasnoća u slušalicama pojačana je dulje nego što se preporučuje"</string>
+ <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Zvuk u slušalicama bio je preglasan dulje nego što se preporučuje"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Glasnoća slušalica premašila je sigurno ograničenje za ovaj tjedan"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Nastavi slušati"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Stišaj"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra veza"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS putem satelita"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hitni pozivi ili SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Poslovni profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Ugađanje korisničkog sučelja sustava pruža vam dodatne načine za prilagodbu korisničkog sučelja Androida. Te se eksperimentalne značajke mogu promijeniti, prekinuti ili nestati u budućim izdanjima. Nastavite uz oprez."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za povratak"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za otvaranje početnog zaslona"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tipka za radnju"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 36bae57..773d9ba 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> és más nyitott alkalmazások észlelték ezt a képernyőképet."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Hozzáadás jegyzethez"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Linkkel együtt"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Képernyőrögzítő"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Képernyőrögzítés feldolgozása"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Folyamatban lévő értesítés képernyőrögzítési munkamenethez"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Képernyővédő"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne zavarjanak"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritási módok"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nem áll rendelkezésre párosított eszköz"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Koppintson egy eszköz csatlakoztatásához vagy leválasztásához"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth használata"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Csatlakozva"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Hang megosztása"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Koppintással átkapcsolhatja vagy megoszthatja a hangot"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"leválasztás"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiválás"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Beállítások megnyitása"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Más eszköz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Áttekintés be- és kikapcsolása"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritási módok"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kész"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Beállítások"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Be"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"A funkciót biztosító szolgáltatás hozzáfér majd minden olyan információhoz, amely látható az Ön képernyőjén, illetve amelyet az Ön eszközéről játszanak le rögzítés vagy átküldés közben. Ez olyan információkat is tartalmaz, mint a jelszavak, a fizetési részletek, a fotók, az üzenetek és a lejátszott audiotartalmak."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Alkalmazás megosztása vagy rögzítése"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Megosztja a képernyőjét a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alkalmazással?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Egyetlen alkalmazás megosztása"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"A teljes képernyő megosztása"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Egyetlen alkalmazás megosztása"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"A teljes képernyő megosztása"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"A teljes képernyő megosztása esetén a képernyő teljes tartalma látható lesz a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> számára. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Alkalmazás megosztása közben az adott appban megjelenített vagy lejátszott minden tartalom látható a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> számára. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Képernyő megosztása"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Műhold, jó kapcsolat"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Műhold, van rendelkezésre álló kapcsolat"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Műholdas SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Segélyhívás vagy SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Munkaprofil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Egyeseknek tetszik, másoknak nem"</string>
<string name="tuner_warning" msgid="1861736288458481650">"A Kezelőfelület-hangoló az Android felhasználói felületének szerkesztéséhez és testreszabásához nyújt további megoldásokat. Ezek a kísérleti funkciók változhatnak vagy megsérülhetnek a későbbi kiadásokban, illetve eltűnhetnek azokból. Körültekintően járjon el."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Vissza kézmozdulat"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Kezdőképernyő kézmozdulat"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Műveletbillentyű"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 24e9065..6a64c5d 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g>-ն ու բացված այլ հավելվածներ հայտնաբերել են այս սքրինշոթը։"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ավելացնել նշմանը"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Ներառել հղումը"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Էկրանի տեսագրում"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Էկրանի տեսագրության մշակում"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Էկրանի տեսագրման աշխատաշրջանի ընթացիկ ծանուցում"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Էկրանապահ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Չանհանգստացնել"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Կարևոր ռեժիմներ"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Զուգակցված սարքեր չկան"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Հպեք՝ սարք միացնելու կամ անջատելու համար"</string>
@@ -300,13 +302,14 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Միացնել Bluetooth-ը"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Միացված է"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Աուդիոյի փոխանցում"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Հպեք՝ աուդիոն փոխանջատելու կամ դրանով կիսվելու համար"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Պահված է"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"անջատել"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ակտիվացնել"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"Ավտոմատ միացնել վաղը"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth-ն օգտագործում են, օրինակ, Quick Share և «Գտնել իմ սարքը» գործառույթները"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Փոխանցել աուդիո"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Փոխանցել աուդիոն"</string>
<string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Աուդիոյի փոխանցում"</string>
<string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"անցնել աուդիոյի փոխանցման կարգավորումներ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Բացել կարգավորումները"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Այլ սարք"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Միացնել/անջատել համատեսքը"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Կարևոր ռեժիմներ"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Պատրաստ է"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Կարգավորումներ"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Միացված է"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Տեսագրման և հեռարձակման ընթացքում ծառայությունների մատակարարին հասանելի կլինեն ձեր սարքի էկրանին ցուցադրվող տեղեկությունները և ձեր սարքով նվագարկվող նյութերը։ Սա ներառում է այնպիսի տեղեկություններ, ինչպիսիք են, օրինակ, գաղտնաբառերը, վճարային տվյալները, լուսանկարները, հաղորդագրությունները և նվագարկվող աուդիո ֆայլերը։"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Հավելվածի էկրանի ցուցադրում կամ տեսագրում"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ցուցադրե՞լ ձեր էկրանը <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածով"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Ցուցադրել մեկ հավելված"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Ցուցադրել ամբողջ էկրանը"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Ցուցադրել մեկ հավելված"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ցուցադրել ամբողջ էկրանը"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Երբ ցուցադրում եք ամբողջ էկրանը, տեսանելի կլինի այն ամենը, ինչ ձեր էկրանին տեսանելի է <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Երբ դուք որևէ հավելված եք հեռարձակում, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին տեսանելի կլինի այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ցուցադրել էկրանը"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Արբանյակային լավ կապ"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Հասանելի է արբանյակային կապ"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Շտապ կանչեր կամ SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Աշխատանքային պրոֆիլ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Զվարճանք մեկ՝ որոշակի մարդու համար"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Համակարգի ՕՄ-ի կարգավորիչը հնարավորություն է տալիս հարմարեցնել Android-ի օգտատիրոջ միջերեսը: Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"«Հետ» ժեստ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Հիմնական էկրան անցնելու ժեստ"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Գործողության ստեղն"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index d5f766b..8c58012 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dan aplikasi terbuka lainnya mendeteksi screenshot ini."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Tambahkan ke catatan"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Sertakan link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Perekam Layar"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses perekaman layar"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifikasi yang sedang berjalan untuk sesi rekaman layar"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Jangan Ganggu"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Mode prioritas"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Perangkat yang disandingkan tak tersedia"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ketuk untuk mulai atau berhenti menghubungkan perangkat"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Terhubung"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Berbagi Audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Ketuk untuk beralih atau berbagi audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"berhenti hubungkan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buka Setelan"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Perangkat lainnya"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktifkan Ringkasan"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mode prioritas"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Setelan"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Aktif"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Layanan yang menyediakan fungsi ini akan memiliki akses ke semua informasi yang terlihat di layar atau diputar dari perangkat saat merekam atau melakukan transmisi. Ini mencakup informasi seperti sandi, detail pembayaran, foto, pesan, dan audio yang Anda putar."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Bagikan atau rekam aplikasi"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Bagikan layar dengan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Bagikan satu aplikasi"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Bagikan seluruh layar"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Bagikan satu aplikasi"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bagikan seluruh layar"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"JIka Anda membagikan seluruh layar, semua hal yang ada di layar Anda akan terlihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Jika Anda membagikan aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan terlihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bagikan layar"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, koneksi baik"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, koneksi tersedia"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via Satelit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Panggilan darurat atau SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil kerja"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Tidak semua orang menganggapnya baik"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Penyetel Antarmuka Pengguna Sistem memberikan cara tambahan untuk mengubah dan menyesuaikan antarmuka pengguna Android. Fitur eksperimental ini dapat berubah, rusak, atau menghilang dalam rilis di masa mendatang. Lanjutkan dengan hati-hati."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gestur kembali"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gestur layar utama"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tombol tindakan"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index b3291bb..6b3b6732 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og önnur opin forrit greindu skjámyndina."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Bæta við glósu"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Hafa tengil með"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Skjáupptaka"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Vinnur úr skjáupptöku"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Áframhaldandi tilkynning fyrir skjáupptökulotu"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Skjávari"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ónáðið ekki"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Forgangsstillingar"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Engin pöruð tæki til staðar"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ýttu til að tengja eða aftengja tæki"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Nota Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tengt"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Hljóði deilt"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Ýttu til að skipta um eða deila hljóði"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"aftengja"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"virkja"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Opna stillingar"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annað tæki"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kveikja/slökkva á yfirliti"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Forgangsstillingar"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Lokið"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Stillingar"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Kveikt"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Þjónustan sem býður upp á þennan eiginleika fær aðgang að öllum upplýsingum sem sjást á skjánum eða eru spilaðar í tækinu á meðan upptaka eða vörpun er í gangi, þar á meðal aðgangsorði, greiðsluupplýsingum, myndum, skilaboðum og hljóðefni sem þú spilar."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Deila eða taka upp forrit"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Deila skjánum með <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Deila einu forriti"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Deila öllum skjánum"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Deila einu forriti"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deila öllum skjánum"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Þegar þú deilir öllum skjánum verður allt á skjánum sýnilegt <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Þegar þú deilir forriti er allt sem sést eða er spilað í því forriti sýnilegt <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deila skjá"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Gervihnöttur, góð tenging"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Gervihnöttur, tenging tiltæk"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Gervihnattar-SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Neyðarsímtöl eða SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Vinnusnið"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Þetta er ekki allra"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Fínstillingar kerfisviðmóts gera þér kleift að fínstilla og sérsníða notendaviðmót Android. Þessir tilraunaeiginleikar geta breyst, bilað eða horfið í síðari útgáfum. Gakktu því hægt um gleðinnar dyr."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Bending til að fara til baka"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Bending til að fara á upphafsskjá"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Aðgerðalykill"</string>
diff --git a/packages/SystemUI/res/values-it-feminine/strings.xml b/packages/SystemUI/res/values-it-feminine/strings.xml
index 99b9361..e8dbc40 100644
--- a/packages/SystemUI/res/values-it-feminine/strings.xml
+++ b/packages/SystemUI/res/values-it-feminine/strings.xml
@@ -21,5 +21,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbata da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
<string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbata da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
- <string name="empty_user_name" msgid="3389155775773578300">"Amiche"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it-masculine/strings.xml b/packages/SystemUI/res/values-it-masculine/strings.xml
index 5e78889..5a76304 100644
--- a/packages/SystemUI/res/values-it-masculine/strings.xml
+++ b/packages/SystemUI/res/values-it-masculine/strings.xml
@@ -21,5 +21,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbato da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
<string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbato da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
- <string name="empty_user_name" msgid="3389155775773578300">"Amici"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it-neuter/strings.xml b/packages/SystemUI/res/values-it-neuter/strings.xml
index 0e1c227..09237f5 100644
--- a/packages/SystemUI/res/values-it-neuter/strings.xml
+++ b/packages/SystemUI/res/values-it-neuter/strings.xml
@@ -21,5 +21,4 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbatə da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
<string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbatə da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
- <string name="empty_user_name" msgid="3389155775773578300">"Amicɜ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index b37eff9..958d67b 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -19,7 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4811759950673118541">"UI sistema"</string>
+ <string name="app_label" msgid="4811759950673118541">"UI di sistema"</string>
<string name="battery_low_title" msgid="5319680173344341779">"Attivare il risparmio energetico?"</string>
<string name="battery_low_description" msgid="3282977755476423966">"Batteria rimanente: <xliff:g id="PERCENTAGE">%s</xliff:g>. Il risparmio energetico attiva il tema scuro, limita l\'attività in background e ritarda le notifiche."</string>
<string name="battery_low_intro" msgid="5148725009653088790">"Il risparmio energetico attiva il tema scuro, limita l\'attività in background e ritarda le notifiche."</string>
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e altre app aperte hanno rilevato questo screenshot."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Aggiungi alla nota"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Includi link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Registrazione dello schermo"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaborazione registrazione…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Salvaschermo"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Non disturbare"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modalità priorità"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nessun dispositivo accoppiato disponibile"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tocca per connettere o disconnettere un dispositivo"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usa il Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Dispositivo connesso"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Condivisione audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tocca per cambiare o condividere l\'audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnetti"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"attiva"</string>
@@ -390,7 +393,7 @@
<string name="performance" msgid="6552785217174378320">"Prestazioni"</string>
<string name="user_interface" msgid="3712869377953950887">"Interfaccia utente"</string>
<string name="thermal" msgid="6758074791325414831">"Termico"</string>
- <string name="custom" msgid="3337456985275158299">"Personalizzate"</string>
+ <string name="custom" msgid="3337456985275158299">"Personalizzata"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Impostazioni di traccia personalizzate"</string>
<string name="restore_default" msgid="5259420807486239755">"Ripristina predefinite"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Apri Impostazioni"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Altro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Attiva/disattiva la panoramica"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modalità priorità"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fine"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Impostazioni"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Il servizio che offre questa funzione avrà accesso a tutte le informazioni visibili sul tuo schermo o riprodotte dal tuo dispositivo durante la registrazione o la trasmissione. Sono incluse informazioni quali password, dettagli sui pagamenti, foto, messaggi e audio riprodotto."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Condividi o registra un\'app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Condividere lo schermo con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Condividi un\'app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Condividi schermo intero"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Condividi un\'app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Condividi schermo intero"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando condividi lo schermo intero, tutto ciò che è nella schermata è visibile a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando condividi un\'app, tutto ciò che viene mostrato o riprodotto al suo interno è visibile a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Condividi schermo"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellitare, connessione buona"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellitare, connessione disponibile"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS satellitare"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chiamate di emergenza o SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profilo di lavoro"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Il divertimento riservato a pochi eletti"</string>
<string name="tuner_warning" msgid="1861736288458481650">"L\'Ottimizzatore UI di sistema mette a disposizione altri metodi per modificare e personalizzare l\'interfaccia utente di Android. Queste funzioni sperimentali potrebbero cambiare, interrompersi o scomparire nelle versioni successive. Procedi con cautela."</string>
@@ -1214,7 +1221,7 @@
<string name="video_status" msgid="4548544654316843225">"Visione in corso"</string>
<string name="audio_status" msgid="4237055636967709208">"Ascolto in corso"</string>
<string name="game_status" msgid="1340694320630973259">"Gioco in corso"</string>
- <string name="empty_user_name" msgid="3389155775773578300">"Persone amiche"</string>
+ <string name="empty_user_name" msgid="3389155775773578300">"Amico"</string>
<string name="empty_status" msgid="5938893404951307749">"Chattiamo stasera."</string>
<string name="status_before_loading" msgid="1500477307859631381">"I contenuti verranno mostrati a breve"</string>
<string name="missed_call" msgid="4228016077700161689">"Chiamata persa"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto Indietro"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto Home"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tasto azione"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index ecb8409..1161fa0 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"האפליקציה <xliff:g id="APPNAME">%1$s</xliff:g> ואפליקציות פתוחות נוספות זיהו את צילום המסך הזה."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"הוספה לפתק"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"הכנסת הקישור"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"מקליט המסך"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"מתבצע עיבוד של הקלטת מסך"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"התראה מתמשכת לסשן הקלטת מסך"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"שומר מסך"</string>
<string name="ethernet_label" msgid="2203544727007463351">"אתרנט"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"נא לא להפריע"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"מצבי עדיפות"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"אין מכשירים מותאמים זמינים"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"אפשר להקיש כדי להתחבר למכשיר או להתנתק ממנו"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"שימוש ב-Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"מחובר"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"שיתוף אודיו"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"צריך להקיש כדי להחליף מצב או לשתף אודיו"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string>
@@ -380,8 +383,8 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"הקלטת המסך"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"התחלה"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"עצירה"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"תיעוד הבעיה"</string>
- <string name="qs_record_issue_start" msgid="2979831312582567056">"התחלה"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"תיעוד בעיה"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"קדימה"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"עצירה"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"דיווח על באג"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"איזה חלק בחוויית השימוש שלך במכשיר הושפע?"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"פתיחת ההגדרות"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"מכשיר אחר"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"החלפת מצב של מסכים אחרונים"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"מצבי עדיפות"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"סיום"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"הגדרות"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"מצב מופעל"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"לשירות שמספק את הפונקציה הזו תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או הפעלת Cast – כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"שיתוף או הקלטה של אפליקציה"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"לשתף את המסך שלך עם <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"שיתוף של אפליקציה אחת"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"שיתוף כל המסך"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"שיתוף של אפליקציה אחת"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"שיתוף כל המסך"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"כשמשתפים את כל המסך, כל מה שמופיע בו יהיה גלוי ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"כשמשתפים אפליקציה, כל מה שרואים או מפעילים בה יהיה גלוי ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"שיתוף המסך"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"לוויין, חיבור באיכות טובה"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"לוויין, יש חיבור זמין"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"תקשורת לוויינית למצב חירום"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"שיחות חירום או SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"פרופיל עבודה"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"מהנה בשביל חלק מהאנשים, אבל לא בשביל כולם"</string>
<string name="tuner_warning" msgid="1861736288458481650">"התכונה System UI Tuner מספקת לך דרכים נוספות להתאים אישית את ממשק המשתמש של Android. התכונות הניסיוניות האלה עשויות להשתנות, לא לעבוד כראוי או להיעלם בגרסאות עתידיות. יש להמשיך בזהירות."</string>
@@ -996,7 +1003,7 @@
<string name="slice_permission_text_1" msgid="6675965177075443714">"- תהיה לה אפשרות לקרוא מידע מאפליקציית <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="slice_permission_text_2" msgid="6758906940360746983">"- תהיה לה יכולת לנקוט פעולה בתוך <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="slice_permission_checkbox" msgid="4242888137592298523">"יש לאשר לאפליקציית <xliff:g id="APP">%1$s</xliff:g> להראות חלקים מכל אפליציה שהיא"</string>
- <string name="slice_permission_allow" msgid="6340449521277951123">"אני רוצה לאשר"</string>
+ <string name="slice_permission_allow" msgid="6340449521277951123">"אישור"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"אני לא מרשה"</string>
<string name="auto_saver_title" msgid="6873691178754086596">"יש להקיש כדי לתזמן את מצב החיסכון בסוללה"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"מומלץ להפעיל את התכונה כשיש סבירות גבוהה שהסוללה תתרוקן"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"תנועת חזרה"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"תנועת חזרה למסך הבית"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"מקש הפעולה"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 98d320d..6436cdd 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> とその他の開いているアプリがこのスクリーンショットを検出しました。"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"メモに追加"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"リンクを含める"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"スクリーン レコーダー"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"画面の録画を処理しています"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"画面の録画セッション中の通知"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"スクリーン セーバー"</string>
<string name="ethernet_label" msgid="2203544727007463351">"イーサネット"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"サイレント モード"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先モード"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ペア設定されたデバイスがありません"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"タップしてデバイスを接続または接続解除します"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth を使用"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"接続しました"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音声の共有"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"タップして音声の切り替えや共有を行えます"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存済み"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"接続を解除"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"有効化"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"設定を開く"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"その他のデバイス"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"概要を切り替え"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先モード"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完了"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ON"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"この機能を提供するサービスは、録画中またはキャスト中に画面上に表示または再生される情報のすべてにアクセスできるようになります。これには、パスワード、お支払いの詳細、写真、メッセージ、音声などが含まれます。"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"アプリの共有または録画"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> と画面を共有しますか?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"1 つのアプリを共有"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"画面全体を共有"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"1 個のアプリを共有"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"画面全体を共有"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"画面全体を共有すると、画面に表示される内容が <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> にすべて公開されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"アプリを共有すると、そのアプリで表示または再生される内容が <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> にすべて公開されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"画面を共有"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛生、接続状態良好"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛生、接続利用可能"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"衛星 SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"緊急通報または SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"仕事用プロファイル"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"一部の方のみお楽しみいただける限定公開ツール"</string>
<string name="tuner_warning" msgid="1861736288458481650">"システムUI調整ツールでは、Androidユーザーインターフェースの調整やカスタマイズを行えます。これらの試験運用機能は今後のリリースで変更となったり、中止となったり、削除されたりする可能性がありますのでご注意ください。"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"「戻る」ジェスチャー"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"「ホーム」ジェスチャー"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"アクションキー"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index d85d7c1..7d52760 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g>-მა და სხვა გახსნილმა აპებმა აღმოაჩინეს ეკრანის ეს ანაბეჭდი."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"დაამატეთ შენიშვნა"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"ბმულის დართვა"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"ეკრანის ჩამწერი"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ეკრანის ჩანაწერი მუშავდება"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"უწყვეტი შეტყობინება ეკრანის ჩაწერის სესიისთვის"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"ეკრანმზოგი"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ეთერნეტი"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"არ შემაწუხოთ"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"პრიორიტეტული რეჟიმები"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"დაწყვილებული მოწყობილობები მიუწვდომელია"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"შეეხეთ მოწყობილობის დასაკავშირებლად ან გასათიშად"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ის გამოყენება"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"დაკავშირებული"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"აუდიოს გაზიარება"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"შეეხეთ აუდიოს გადასართავად ან გასაზიარებლად"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"შენახული"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"კავშირის გაწყვეტა"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"გააქტიურება"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"პარამეტრების გახსნა"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"სხვა მოწყობილობა"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"მიმოხილვის გადართვა"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"პრიორიტეტული რეჟიმები"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"მზადაა"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"პარამეტრები"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ჩართული"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ამ ფუნქციის მომწოდებელ სერვისს ექნება წვდომა ყველა ინფორმაციაზე, რომელიც თქვენს ეკრანზე გამოჩნდება ან თქვენს მოწყობილობაზე დაიკვრება ჩაწერის ან ტრანსლირების განმავლობაში. აღნიშნული მოიცავს ისეთ ინფორმაციას, როგორიც არის პაროლები, გადახდის დეტალები, ფოტოები, შეტყობინებები და თქვენ მიერ დაკრული აუდიო."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"აპის გაზიარება ან ჩაწერა"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"გსურთ თქვენი ეკრანის <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-თან გაზიარება?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ერთი აპის გაზიარება"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"მთლიანი ეკრანის გაზიარება"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ერთი აპის გაზიარება"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"მთლიანი ეკრანის გაზიარება"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"მთლიანი ეკრანის გაზიარებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ხედავს ყველაფერს, რაც თქვენს ეკრანზეა. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"აპის გაზიარებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ხედავს ყველაფერს, რაც ჩანს ან უკრავს ამ აპში. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ეკრანის გაზიარება"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"კარგი სატელიტური კავშირი"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ხელმისაწვდომია სატელიტური კავშირი"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"სატელიტური SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"გადაუდებელი ზარი ან SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"სამსახურის პროფილი"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ზოგისთვის გასართობია, მაგრამ არა ყველასთვის"</string>
<string name="tuner_warning" msgid="1861736288458481650">"სისტემის UI ტუნერი გაძლევთ დამატებით გზებს Android-ის სამომხმარებლო ინტერფეისის პარამეტრების დაყენებისთვის. ეს ექსპერიმენტული მახასიათებლები შეიძლება შეიცვალოს, შეწყდეს ან გაქრეს მომავალ ვერსიებში. სიფრთხილით გააგრძელეთ."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"უკან დაბრუნების ჟესტი"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"მთავარ ეკრანზე გადასვლის ჟესტი"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"მოქმედების კლავიში"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 7a43b4b..7b32db1 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> және басқа да ашық қолданбалар осы скриншотты анықтады."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ескертпеге қосу"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Сілтеме қосу"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Экран жазғыш"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экран жазғыш бейнесін өңдеу"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды бейнеге жазудың ағымдағы хабарландыруы"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Скринсейвер"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Этернет"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Мазаламау"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Басымдық режимдері"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Жұптасқан құрылғылар жоқ"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Құрылғыны жалғау не ажырату үшін түртіңіз."</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ты пайдалану"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Қосылды"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Аудио бөлісу"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Аудионы бөлісу немесе ауыстыру үшін түртіңіз."</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сақталды"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажырату"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"іске қосу"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Параметрлерді ашу"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Басқа құрылғы"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Шолуды қосу/өшіру"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Басымдық режимдері"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Дайын"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Параметрлер"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Қосулы"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Осы функцияны ұсынатын қызмет экранда көрсетілетін немесе жазу не трансляциялау кезінде құрылғыда ойнатылған барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және ойнатылатын аудио сияқты ақпарат кіреді."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Қолданба экранын бөлісу не жазу"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Экранды <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасымен бөлісу керек пе?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Бір қолданбаны бөлісу"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Бүкіл экранды бөлісу"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Бір қолданбаны бөлісу"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Бүкіл экранды бөлісу"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Бүкіл экранды бөліскен кезде, ондағы барлық контент <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасында көрсетіледі. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды, фотосуреттерді және аудио мен бейнені ашқанда сақ болыңыз."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Қолданбаны бөліскен кезде, онда көрінетін не ойнатылатын барлық контент <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасында көрсетіледі. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды, фотосуреттерді және аудио мен бейнені ашқанда сақ болыңыз."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлісу"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Жерсерік, байланыс жақсы."</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Жерсерік, байланыс бар."</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Құтқару қызметіне қоңырау шалу немесе SOS сигналын жіберу"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Жұмыс профилі"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Кейбіреулерге қызық, бірақ барлығына емес"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Жүйелік пайдаланушылық интерфейс тюнері Android пайдаланушылық интерфейсін реттеудің қосымша жолдарын береді. Бұл эксперименттік мүмкіндіктер болашақ шығарылымдарда өзгеруі, бұзылуы немесе жоғалуы мүмкін. Сақтықпен жалғастырыңыз."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Артқа қайтару қимылы"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Негізгі бетке қайтару қимылы"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Әрекет пернесі"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 2319623..81664ca 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> និងកម្មវិធីដែលបើកផ្សេងទៀតបានរកឃើញរូបថតអេក្រង់នេះ។"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"បញ្ចូលទៅក្នុងកំណត់ចំណាំ"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"រួមបញ្ចូលតំណ"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"មុខងារថតវីដេអូអេក្រង់"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"កំពុងដំណើរការការថតអេក្រង់"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ការជូនដំណឹងដែលកំពុងដំណើរការសម្រាប់រយៈពេលប្រើការថតសកម្មភាពអេក្រង់"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"ធាតុរក្សាអេក្រង់"</string>
<string name="ethernet_label" msgid="2203544727007463351">"អ៊ីសឺរណិត"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"កុំរំខាន"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"មុខងារអាទិភាព"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ប៊្លូធូស"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"មិនមានឧបករណ៍ផ្គូផ្គងដែលអាចប្រើបាន"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ចុចដើម្បីភ្ជាប់ ឬផ្ដាច់ឧបករណ៍"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ប្រើប៊្លូធូស"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"បានភ្ជាប់"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ការស្ដាប់សំឡេងរួមគ្នា"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ចុចដើម្បីប្ដូរ ឬចែករំលែកសំឡេង"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"បានរក្សាទុក"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ផ្ដាច់"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"បើកដំណើរការ"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"បើកការកំណត់"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ឧបករណ៍ផ្សេងទៀត"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"បិទ/បើកទិដ្ឋភាពរួម"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"មុខងារអាទិភាព"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"រួចរាល់"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ការកំណត់"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"បើក"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"សេវាកម្មដែលផ្ដល់មុខងារនេះនឹងមានសិទ្ធិចូលប្រើព័ត៌មានទាំងអស់ដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬចាក់ពីឧបករណ៍របស់អ្នក នៅពេលកំពុងថត ឬភ្ជាប់។ ព័ត៌មាននេះមានដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ រូបថត សារ និងសំឡេងដែលអ្នកចាក់ជាដើម។"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ចែករំលែក ឬថតកម្មវិធី"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"បង្ហាញអេក្រង់របស់អ្នកជាមួយ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ឬ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"បង្ហាញកម្មវិធីមួយ"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"បង្ហាញអេក្រង់ទាំងមូល"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"បង្ហាញកម្មវិធីមួយ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"បង្ហាញអេក្រង់ទាំងមូល"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"នៅពេលអ្នកបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នក <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មើលឃើញអ្វីគ្រប់យ៉ាងនៅលើអេក្រង់របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"នៅពេលអ្នកបង្ហាញកម្មវិធីណាមួយ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មើលឃើញអ្វីគ្រប់យ៉ាងដែលបង្ហាញ ឬចាក់ក្នុងកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"បង្ហាញអេក្រង់"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ផ្កាយរណប មានការតភ្ជាប់ល្អ"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ផ្កាយរណប អាចតភ្ជាប់បាន"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"ការប្រកាសអាសន្នតាមផ្កាយរណប"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ការហៅទៅលេខសង្គ្រោះបន្ទាន់ ឬ SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"កម្រងព័ត៌មានការងារ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ល្អសម្រាប់អ្នកប្រើមួយចំនួន តែមិនសម្រាប់គ្រប់គ្នាទេ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"កម្មវិធីសម្រួល UI ប្រព័ន្ធផ្តល់ជូនអ្នកនូវមធ្យោបាយបន្ថែមទៀតដើម្បីកែសម្រួល និងប្តូរចំណុចប្រទាក់អ្នកប្រើ Android តាមបំណង។ លក្ខណៈពិសេសសាកល្បងនេះអាចនឹងផ្លាស់ប្តូរ បំបែក ឬបាត់បង់បន្ទាប់ពីការចេញផ្សាយនាពេលអនាគត។ សូមបន្តដោយប្រុងប្រយ័ត្ន។"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ចលនាថយក្រោយ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ចលនាទៅទំព័រដើម"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"គ្រាប់ចុចសកម្មភាព"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index aeb3b1ef0..e1b119c 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -45,7 +45,7 @@
<string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಅನ್ನು ನಿಯಂತ್ರಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯುವುದೇ?\nಈ ಆ್ಯಪ್ಗೆ ರೆಕಾರ್ಡ್ ಅನುಮತಿಯನ್ನು ನೀಡಲಾಗಿಲ್ಲ, ಆದರೆ ಈ USB ಸಾಧನದ ಮೂಲಕ ಆಡಿಯೊವನ್ನು ಸೆರೆಹಿಡಿಯಬಹುದು."</string>
<string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯುವುದೇ?"</string>
<string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"ಆಪ್ಗಳು USB ಪರಿಕರದಲ್ಲಿ ಕಾರ್ಯನಿರ್ವಹಿಸುವುದಿಲ್ಲ. ಆ ಬಗ್ಗೆ <xliff:g id="URL">%1$s</xliff:g> ನಲ್ಲಿ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
- <string name="title_usb_accessory" msgid="1236358027511638648">"USB ಪರಿಕರ"</string>
+ <string name="title_usb_accessory" msgid="1236358027511638648">"USB ಆ್ಯಕ್ಸೆಸರಿ"</string>
<string name="label_view" msgid="6815442985276363364">"ವೀಕ್ಷಿಸು"</string>
<string name="always_use_device" msgid="210535878779644679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಸಂಪರ್ಕಗೊಂಡಾಗ ಯಾವಾಗಲೂ <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="always_use_accessory" msgid="1977225429341838444">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ಸಂಪರ್ಕಗೊಂಡಾಗ ಯಾವಾಗಲೂ <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string>
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ಹಾಗೂ ತೆರೆದಿರುವ ಇತರ ಆ್ಯಪ್ಗಳು ಈ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ಪತ್ತೆಹಚ್ಚಿವೆ."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ಟಿಪ್ಪಣಿಗೆ ಸೇರಿಸಿ"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"ಲಿಂಕ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೆಶನ್ಗಾಗಿ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ನೋಟಿಫಿಕೇಶನ್"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"ಸ್ಕ್ರೀನ್ ಸೇವರ್"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ಇಥರ್ನೆಟ್"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ಆದ್ಯತೆಯ ಮೋಡ್ಗಳು"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ಬ್ಲೂಟೂತ್"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ಯಾವುದೇ ಜೋಡಿಸಲಾದ ಸಾಧನಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ಸಾಧನವನ್ನು ಕನೆಕ್ಟ್ ಅಥವಾ ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ಬ್ಲೂಟೂತ್ ಬಳಸಿ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ಆಡಿಯೊವನ್ನು ಬದಲಾಯಿಸಲು ಅಥವಾ ಹಂಚಿಕೊಳ್ಳಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ಅನ್ಯ ಸಾಧನ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ಟಾಗಲ್ ನ ಅವಲೋಕನ"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ಆದ್ಯತೆಯ ಮೋಡ್ಗಳು"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ಮುಗಿದಿದೆ"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ಆನ್ ಆಗಿದೆ"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ಈ ಕಾರ್ಯವನ್ನು ಒದಗಿಸುವ ಸೇವೆಗಳು, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುವ ಅಥವಾ ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುವಾಗ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡುವ ಎಲ್ಲಾ ಮಾಹಿತಿಗಳಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತವೆ. ಇದು ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ನೀವು ಪ್ಲೇ ಮಾಡುವ ಆಡಿಯೊದಂತಹ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ ಅಥವಾ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಅನ್ನು <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ನೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಬೇಕೇ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನೀವು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನಲ್ಲಿರುವ ಏನಾದರೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಗೆ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿದ ಏನಾದರೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಗೆ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳಿ"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ಸ್ಯಾಟಲೈಟ್, ಕನೆಕ್ಷನ್ ಉತ್ತಮವಾಗಿದೆ"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ಸ್ಯಾಟಲೈಟ್, ಕನೆಕ್ಷನ್ ಲಭ್ಯವಿದೆ"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"ಸ್ಯಾಟಲೈಟ್ SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ತುರ್ತು ಕರೆಗಳು ಅಥವಾ SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ಕೆಲವರಿಗೆ ಮೋಜು ಆಗಿದೆ ಎಲ್ಲರಿಗೆ ಇಲ್ಲ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"ಸಿಸ್ಟಂ UI ಟ್ಯೂನರ್ ನಿಮಗೆ Android ಬಳಕೆದಾರ ಅಂತರಸಂಪರ್ಕವನ್ನು ಸರಿಪಡಿಸಲು ಮತ್ತು ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹೆಚ್ಚುವರಿ ಮಾರ್ಗಗಳನ್ನು ನೀಡುತ್ತದೆ. ಈ ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯಗಳು ಭವಿಷ್ಯದ ಬಿಡುಗಡೆಗಳಲ್ಲಿ ಬದಲಾಗಬಹುದು, ವಿರಾಮವಾಗಬಹುದು ಅಥವಾ ಕಾಣಿಸಿಕೊಳ್ಳದಿರಬಹುದು. ಎಚ್ಚರಿಕೆಯಿಂದ ಮುಂದುವರಿಯಿರಿ."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ಹಿಂಬದಿ ಗೆಸ್ಚರ್"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ಹೋಮ್ ಗೆಸ್ಚರ್"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ಆ್ಯಕ್ಷನ್ ಕೀ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 277582c..27aa785 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> 및 기타 공개 앱에서 이 스크린샷을 감지했습니다."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"메모에 추가"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"링크 포함"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g><xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"화면 녹화"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"화면 녹화 처리 중"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"화면 녹화 세션에 관한 지속적인 알림"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"화면 보호기"</string>
<string name="ethernet_label" msgid="2203544727007463351">"이더넷"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"방해 금지 모드"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"우선순위 모드"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"블루투스"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"페어링된 기기가 없습니다"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"기기를 연결 또는 연결 해제하려면 탭하세요"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"블루투스 사용"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"연결됨"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"오디오 공유"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"오디오를 전환하거나 공유하려면 탭하세요"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"저장됨"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"연결 해제"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"실행"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"설정 열기"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"기타 기기"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"최근 사용 버튼 전환"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"우선순위 모드"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"완료"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"설정"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"사용"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"이 기능을 제공하는 서비스는 녹화 또는 전송 중에 화면에 표시되거나 기기에서 재생되는 모든 정보에 액세스할 수 있습니다. 여기에는 비밀번호, 결제 세부정보, 사진, 메시지, 사용자가 재생하는 오디오 등의 정보가 포함됩니다."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"앱 공유 또는 녹화"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"화면을 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱과 공유하시겠습니까?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"앱 하나 공유"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"전체 화면 공유"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"앱 하나 공유"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"전체 화면 공유"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"전체 화면을 공유하면 화면에 있는 모든 항목이 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"앱을 공유하면 앱에 표시되거나 앱에서 재생되는 모든 항목이 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"화면 공유"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"위성, 연결 상태 양호"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"위성, 연결 가능"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"위성 긴급 SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"긴급 전화 또는 SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"직장 프로필"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"마음에 들지 않을 수도 있음"</string>
<string name="tuner_warning" msgid="1861736288458481650">"시스템 UI 튜너를 사용하면 Android 사용자 인터페이스를 변경 및 맞춤설정할 수 있습니다. 이러한 실험실 기능은 향후 출시 버전에서는 변경되거나 다운되거나 사라질 수 있습니다. 신중하게 진행하시기 바랍니다."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"뒤로 동작"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"홈 동작"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"작업 키"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 4680236..306baa7 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -104,13 +104,14 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> жана ачылып турган башка колдонмолор ушул скриншотту аныктады."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Кыска жазууга кошуу"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Шилтеме кошуу"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Экрандан видео жаздырып алуу"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экрандан жаздырылып алынган видео иштетилүүдө"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды жаздыруу сеансы боюнча учурдагы билдирме"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Экранды жаздырасызбы?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бир колдонмону жаздыруу"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтүндөй экранды жаздыруу"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Бүт экранды жаздырганда экранда көрүнүп турган нерселердин баары жаздырылат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Бүтүндөй экранды жаздырганда, андагы нерселердин баары видеого түшүп калат. Андыктан этият болуп, сырсөздөр, төлөм ыкмалары, билдирүүлөр, сүрөттөр, аудио жана видео материалдар сыяктуу купуя нерселерди көрсөтүп албаңыз."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Колдонмону жаздырганда ал колдонмодо көрсөтүлүп же ойнотулуп жаткан нерселер жаздырылат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Экранды жаздыруу"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Жаздыруу үчүн колдонмо тандоо"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Көшөгө"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Тынчымды алба"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Маанилүүлүк режимдери"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Жупташкан түзмөктөр жок"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Түзмөктү туташтыруу же ажыратуу үчүн таптаңыз"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Иштетүү"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Туташты"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Чогуу угуу"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Аудиону которуштуруу же бөлүшүү үчүн таптаңыз"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сакталды"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажыратуу"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"иштетүү"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Параметрлерди ачуу"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Башка түзмөк"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Назар режимин өчүрүү/күйгүзүү"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Маанилүүлүк режимдери"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Бүттү"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Параметрлер"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Күйүк"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Жаздырып же тышкы экранга чыгарып жатканда кызмат көрсөтүүчү экраныңыздагы бардык маалыматты же түзмөктө ойнотулуп жаткан нерселерди көрө алат. Буга сырсөздөр, төлөмдүн чоо-жайы, сүрөттөр, билдирүүлөр жана ойнотулган аудио кирет."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Колдонмону бөлүшүү же жаздыруу"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Экранды <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> менен бөлүшөсүзбү?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Бир колдонмону бөлүшүү"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Толук экранды бөлүшүү"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Бир колдонмону бөлүшүү"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Толук экранды бөлүшүү"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Бүтүндөй экранды бөлүшкөндө андагы бардык нерселер <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна көрүнөт. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Колдонмону бөлүшкөндө ал колдонмодо көрсөтүлүп же ойнотулуп жаткан нерселер <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна көрүнөт. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлүшүү"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спутник, байланыш жакшы"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спутник, байланыш бар"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Спутник SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Шашылыш чалуулар же SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Жумуш профили"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Баарына эле жага бербейт"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner Android колдонуучу интерфейсин жөнгө салып жана ыңгайлаштыруунун кошумча ыкмаларын сунуштайт. Бул сынамык функциялар кийинки чыгарылыштарда өзгөрүлүп, бузулуп же жоголуп кетиши мүмкүн. Абайлап колдонуңуз."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Артка кайтуу жаңсоосу"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Башкы бетке өтүү жаңсоосу"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Аракет баскычы"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index edb8fa3..75f579c 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ແລະ ແອັບອື່ນໆທີ່ເປີດຢູ່ກວດພົບຮູບໜ້າຈໍນີ້."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ເພີ່ມໃສ່ບັນທຶກ"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"ຮວມລິ້ງ"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ກຳລັງປະມວນຜົນການບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ການແຈ້ງເຕືອນສຳລັບເຊດຊັນການບັນທຶກໜ້າຈໍໃດໜຶ່ງ"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"ພາບພັກໜ້າຈໍ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ຫ້າມລົບກວນ"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ໂໝດຄວາມສຳຄັນ"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ບໍ່ມີອຸປະກອນທີ່ສາມາດຈັບຄູ່ໄດ້"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ແຕະເພື່ອເຊື່ອມຕໍ່ ຫຼື ຕັດການເຊື່ອມຕໍ່ອຸປະກອນ"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ໃຊ້ Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ການແບ່ງປັນສຽງ"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ແຕະເພື່ອສະຫຼັບ ຫຼື ແບ່ງປັນສຽງ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ບັນທຶກແລ້ວ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ຕັດການເຊື່ອມຕໍ່"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ເປີດນຳໃຊ້"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ເປີດການຕັ້ງຄ່າ"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ອຸປະກອນອື່ນໆ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ສະຫຼັບພາບຮວມ"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ໂໝດຄວາມສຳຄັນ"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ແລ້ວໆ"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ການຕັ້ງຄ່າ"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ເປີດ"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ບໍລິການທີ່ມີຟັງຊັນນີ້ຈະມີສິດເຂົ້າເຖິງຂໍ້ມູນທັງໝົດທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຈາກອຸປະກອນຂອງທ່ານໃນຂະນະທີ່ບັນທຶກ ຫຼື ສົ່ງສັນຍານ. ເຊິ່ງຈະຮວມທັງຂໍ້ມູນຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຮູບພາບ, ຂໍ້ຄວາມ ແລະ ສຽງທີ່ທ່ານຫຼິ້ນ."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ແບ່ງປັນ ຫຼື ບັນທຶກແອັບ"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"ແບ່ງປັນໜ້າຈໍຂອງທ່ານກັບ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ບໍ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ແບ່ງປັນແອັບດຽວ"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"ແບ່ງປັນໜ້າຈໍທັງໝົດ"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ແບ່ງປັນແອັບດຽວ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ແບ່ງປັນໜ້າຈໍທັງໝົດ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ເມື່ອທ່ານແບ່ງປັນໜ້າຈໍທັງໝົດຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ຢູ່ໜ້າຈໍຂອງທ່ານໃນ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ເມື່ອທ່ານແບ່ງປັນແອັບຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ສະແດງ ຫຼື ຫຼິ້ນໃນແອັບໃນ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ແບ່ງປັນໜ້າຈໍ"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ດາວທຽມ, ການເຊື່ອມຕໍ່ດີ"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ດາວທຽມ, ການເຊື່ອມຕໍ່ທີ່ພ້ອມນຳໃຊ້"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS ດາວທຽມ"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ໂທສຸກເສີນ ຫຼື SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ມ່ວນຊື່ນສຳລັບບາງຄົນ ແຕ່ບໍ່ແມ່ນສຳລັບທຸກຄົນ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner ໃຫ້ທ່ານມີວິທີພິເສດຕື່ມອີກໃນການປັບປ່ຽນ ແລະຕົບແຕ່ງສ່ວນຕໍ່ປະສານຜູ້ໃຊ້ຂອງ Android. ຄຸນສົມບັດທົດລອງໃຊ້ເຫຼົ່ານີ້ອາດຈະປ່ຽນແປງ, ຢຸດເຊົາ ຫຼືຫາຍໄປໃນການວາງຈຳໜ່າຍໃນອະນາຄົດ. ຈົ່ງດຳເນີນຕໍ່ດ້ວຍຄວາມລະມັດລະວັງ."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ທ່າທາງສຳລັບກັບຄືນ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ທ່າທາງສຳລັບໜ້າຫຼັກ"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ປຸ່ມຄຳສັ່ງ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a9a0e73..61516fe 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ ir kitos atidarytos programos aptiko šią ekrano kopiją."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Pridėti prie užrašo"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Įtraukti nuorodą"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Ekrano vaizdo įrašytuvas"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Apdorojam. ekrano vaizdo įraš."</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Šiuo metu rodomas ekrano įrašymo sesijos pranešimas"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Ekrano užsklanda"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Eternetas"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Netrukdymo režimas"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteto režimai"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nėra pasiekiamų susietų įrenginių"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Palieskite, kad prijungtumėte ar atjungtumėte įrenginį"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"„Bluetooth“ naudojimas"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Prisijungta"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Garso įrašų bendrinimas"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Palieskite, jei norite perjungti arba bendrinti garsą"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Išsaugota"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atjungti"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"suaktyvinti"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Atidaryti nustatymus"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Kitas įrenginys"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Perjungti apžvalgą"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteto režimai"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Atlikta"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nustatymai"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Įjungta"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Šią funkciją teikianti paslauga galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Programos bendrinimas ar įrašymas"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Bendrinti ekraną su „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Bendrinti vieną programą"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Bendrinti visą ekraną"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Bendrinti vieną programą"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bendrinti visą ekraną"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kai bendrinate visą ekraną, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ matomas visas ekrano turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kai bendrinate programą, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ matomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bendrinti ekraną"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Palydovas, geras ryšys"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Palydovas, pasiekiamas ryšys"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Prisijungimas prie palydovo kritiniu atveju"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Skambučiai pagalbos numeriu arba pagalbos iškvietimas kritiniu atveju"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Darbo profilis"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Smagu, bet ne visada"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sistemos naudotojo sąsajos derinimo priemonė suteikia papildomų galimybių pagerinti ir tinkinti „Android“ naudotojo sąsają. Šios eksperimentinės funkcijos gali pasikeisti, nutrūkti ar išnykti iš būsimų laidų. Tęskite atsargiai."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Grįžimo atgal gestas"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pagrindinio ekrano gestas"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Veiksmų klavišas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index d1e97a6..1b48d3f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> un citas atvērtas lietotnes konstatēja, ka tika veikts ekrānuzņēmums."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Pievienot piezīmei"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Iekļaut saiti"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Ekrāna ierakstītājs"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekrāna ieraksta apstrāde"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Aktīvs paziņojums par ekrāna ierakstīšanas sesiju"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Ekrānsaudzētājs"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Tīkls Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Režīms “Netraucēt”"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritātes režīmi"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nav pieejama neviena pārī savienota ierīce."</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Lai pievienotu vai atvienotu kādu ierīci, pieskarieties."</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Izmantot Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Savienojums izveidots"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio kopīgošana"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Pieskarieties, lai pārslēgtu vai kopīgotu audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atvienot"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizēt"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Atvērt iestatījumus"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Cita ierīce"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pārskata pārslēgšana"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritātes režīmi"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gatavs"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Iestatījumi"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Ieslēgts"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Pakalpojums, kas nodrošina šo funkciju, iegūs piekļuvi visai informācijai, kas ierakstīšanas vai apraides laikā tiks rādīta jūsu ekrānā vai atskaņota jūsu ierīcē. Atļauja attiecas uz tādu informāciju kā paroles, maksājumu informācija, fotoattēli, ziņojumi un jūsu atskaņotais audio saturs."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Lietotnes kopīgošana vai ierakstīšana"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vai kopīgot ekrānu ar lietotni <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Kopīgot vienu lietotni"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Kopīgot visu ekrānu"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Kopīgot vienu lietotni"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Kopīgot visu ekrānu"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kopīgojot visu ekrānu, lietotnei <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ir pieejams viss ekrāna saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kopīgojot lietotni, lietotnei <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ir pieejams viss kopīgotajā lietotnē parādītais vai atskaņotais saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Kopīgot ekrānu"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelīts, labs savienojums"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelīts, ir pieejams savienojums"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelīta SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Ārkārtas izsaukumi vai ārkārtas zvani"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Darba profils"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Jautri dažiem, bet ne visiem"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sistēmas saskarnes regulators sniedz papildu veidus, kā mainīt un pielāgot Android lietotāja saskarni. Nākamajās versijās šīs eksperimentālās funkcijas var tikt mainītas, bojātas vai to darbība var tikt pārtraukta. Turpinot esiet uzmanīgs."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Žests pāriešanai atpakaļ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Žests pāriešanai uz sākumu"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Darbību taustiņš"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 7691ae6..1c853df 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> и други отворени апликации ја открија оваа слика од екранот."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Додај во белешка"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Опфати линк"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Снимач на екран"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Се обработува снимка од екран"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Тековно известување за сесија за снимање на екранот"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Штедач на екран"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Етернет"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не вознемирувај"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Нема достапни спарени уреди"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Допрете за да воспоставите или да прекинете врска со уред"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Поврзано"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Споделување аудио"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Допрете за да префрлите или споделите аудио"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Зачувано"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекини врска"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирај"</string>
@@ -389,7 +392,7 @@
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екран"</string>
<string name="performance" msgid="6552785217174378320">"Изведба"</string>
<string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string>
- <string name="thermal" msgid="6758074791325414831">"Термално"</string>
+ <string name="thermal" msgid="6758074791325414831">"Прегревање"</string>
<string name="custom" msgid="3337456985275158299">"Приспособено"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Приспособени поставки за следење"</string>
<string name="restore_default" msgid="5259420807486239755">"Врати на стандардно"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Отворете „Поставки“"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Друг уред"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Вклучи/исклучи преглед"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Поставки"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Вклучено"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Услугата што ја обезбедува функцијава ќе има пристап до сите податоци што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува податоци како лозинките, деталите за плаќање, фотографиите, пораките и аудиото што го пуштате."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Споделете или снимете апликација"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Да се сподели вашиот екран со <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Споделете една апликација"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Споделете го целиот екран"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Споделете една апликација"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Споделете го целиот екран"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Додека го споделувате целиот екран, сè на екранот е видливо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Додека споделувате апликација, сѐ што се прикажува или пушта на таа апликација е видливо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Сподели екран"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Добра сателитска врска"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Достапна е сателитска врска"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Сателитски SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Итни повици или SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Работен профил"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Забава за некои, но не за сите"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Адаптерот на УИ на системот ви дава дополнителни начини за дотерување и приспособување на корисничкиот интерфејс на Android. Овие експериментални функции можеби ќе се изменат, расипат или ќе исчезнат во следните изданија. Продолжете со претпазливост."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Движење за назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Движење за почетен екран"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Копче за дејство"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 8c0378a..ef63d67 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> എന്ന ആപ്പും തുറന്നിരിക്കുന്ന മറ്റ് ആപ്പും ഈ സ്ക്രീൻഷോട്ട് തിരിച്ചറിഞ്ഞു."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"കുറിപ്പിലേക്ക് ചേർക്കുക"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"ലിങ്ക് ഉൾപ്പെടുത്തുക"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"സ്ക്രീൻ റെക്കോർഡർ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"സ്ക്രീൻ റെക്കോർഡിംഗ് പ്രോസസുചെയ്യുന്നു"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ഒരു സ്ക്രീൻ റെക്കോർഡിംഗ് സെഷനായി നിലവിലുള്ള അറിയിപ്പ്"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"സ്ക്രീൻ സേവർ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ഇതർനെറ്റ്"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ശല്യപ്പെടുത്തരുത്"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"മുൻഗണനാ മോഡുകൾ"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ജോടിയാക്കിയ ഉപകരണങ്ങളൊന്നും ലഭ്യമല്ല"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ഒരു ഉപകരണം കണക്റ്റ് ചെയ്യാനോ വിച്ഛേദിക്കാനോ ടാപ്പ് ചെയ്യുക"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ഉപയോഗിക്കുക"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"കണക്റ്റ് ചെയ്തു"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ഓഡിയോ പങ്കിടൽ"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ഓഡിയോ മാറാനോ പങ്കിടാനോ ടാപ്പ് ചെയ്യുക"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"സംരക്ഷിച്ചു"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"വിച്ഛേദിക്കുക"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"സജീവമാക്കുക"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ക്രമീകരണം തുറക്കുക"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"മറ്റ് ഉപകരണം"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"അവലോകനം മാറ്റുക"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"മുൻഗണനാ മോഡുകൾ"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ശരി"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ക്രമീകരണം"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ഓണാണ്"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും ഈ ഫംഗ്ഷൻ ലഭ്യമാക്കുന്ന സേവനത്തിന് ആക്സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഓഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, പാസ്വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ഒരു ആപ്പ് പങ്കിടുക അല്ലെങ്കിൽ റെക്കോർഡ് ചെയ്യുക"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"നിങ്ങളുടെ സ്ക്രീൻ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതുമായി പങ്കിടണോ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ഒരു ആപ്പ് പങ്കിടുക"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"സ്ക്രീൻ മുഴുവനായി പങ്കിടുക"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ആപ്പ് പങ്കിടുക"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"സ്ക്രീൻ പൂർണമായും പങ്കിടുക"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"നിങ്ങളുടെ സ്ക്രീൻ മുഴുവനായി പങ്കിടുമ്പോൾ, സ്ക്രീനിലെ എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ദൃശ്യമാകും. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധ പുലർത്തുക."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"നിങ്ങളുടെ ആപ്പ് പങ്കിടുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ദൃശ്യമാകും. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധ പുലർത്തുക."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"സ്ക്രീൻ പങ്കിടുക"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"സാറ്റലൈറ്റ്, മികച്ച കണക്ഷൻ"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"സാറ്റലൈറ്റ്, കണക്ഷൻ ലഭ്യമാണ്"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"സാറ്റലൈറ്റ് SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"എമർജൻസി കോൾ അല്ലെങ്കിൽ SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ചിലർക്ക് വിനോദം, എന്നാൽ എല്ലാവർക്കുമില്ല"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Android ഉപയോക്തൃ ഇന്റർഫേസ് ആവശ്യമുള്ള രീതിയിൽ മാറ്റുന്നതിനും ഇഷ്ടാനുസൃതമാക്കുന്നതിനും സിസ്റ്റം UI ട്യൂണർ നിങ്ങൾക്ക് അധിക വഴികൾ നൽകുന്നു. ഭാവി റിലീസുകളിൽ ഈ പരീക്ഷണാത്മക ഫീച്ചറുകൾ മാറ്റുകയോ നിർത്തുകയോ അപ്രത്യക്ഷമാവുകയോ ചെയ്തേക്കാം. ശ്രദ്ധയോടെ മുന്നോട്ടുപോകുക."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"\'മടങ്ങുക\' ജെസ്ച്ചർ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ഹോം ജെസ്ച്ചർ"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ആക്ഷൻ കീ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index bfd48a4..ff8c52b 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> болон бусад нээлттэй апп энэ дэлгэцийн агшныг илрүүлсэн."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Тэмдэглэлд нэмэх"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Холбоосыг оруулах"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Дэлгэцийн үйлдэл бичигч"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Дэлгэц бичлэг боловсруулж байна"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Дэлгэц бичих горимын үргэлжилж буй мэдэгдэл"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Дэлгэц амраагч"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Этернет"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Бүү саад бол"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Чухал байдлаар нь ангилах горим"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Хослуулсан төхөөрөмж байхгүй"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Төхөөрөмжийг холбох эсвэл салгахын тулд товшино уу"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-г ашиглах"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Холбогдсон"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Аудио хуваалцах"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Аудиог сэлгэх эсвэл хуваалцахын тулд товшино уу"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Хадгалсан"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"салгах"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"идэвхжүүлэх"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Тохиргоог нээх"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Бусад төхөөрөмж"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг асаах/унтраах"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Чухал байдлаар нь ангилах горим"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Болсон"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Тохиргоо"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Асаалттай"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Энэ функцийг олгож буй үйлчилгээ нь бичлэг хийж эсвэл дамжуулж байх үед таны дэлгэцэд харуулсан эсвэл төхөөрөмжөөс тань тоглуулсан бүх мэдээлэлд хандах эрхтэй. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Апп хуваалцах эсвэл бичих"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Дэлгэцээ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-тай хуваалцах уу?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Нэг апп хуваалцах"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Дэлгэцийг бүтнээр нь хуваалцах"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Нэг апп хуваалцах"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Дэлгэцийг бүтнээр нь хуваалцах"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Таныг дэлгэцээ бүхэлд нь хуваалцаж байхад дэлгэц дээр тань байгаа аливаа зүйл <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-д харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Таныг апп хуваалцаж байхад тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйл <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-д харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Дэлгэцийг хуваалцах"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Хиймэл дагуул, холболт сайн байна"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Хиймэл дагуул, холболт боломжтой"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Хиймэл дагуул SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Яаралтай дуудлага эсвэл SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Ажлын профайл"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Зарим хүнд хөгжилтэй байж болох ч бүх хүнд тийм биш"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Системийн UI Tохируулагч нь Android хэрэглэгчийн интерфэйсийг тааруулах, өөрчлөх нэмэлт аргыг зааж өгөх болно. Эдгээр туршилтын тохиргоо нь цаашид өөрчлөгдөх, эвдрэх, алга болох магадлалтай. Үйлдлийг болгоомжтой хийнэ үү."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Буцах зангаа"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Үндсэн нүүрний зангаа"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Тусгай товчлуур"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index d124ce3..76515d2 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> आणि उघडलेल्या इतर अॅप्सनी हा स्क्रीनशॉट डिटेक्ट केला."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"टीप जोडा"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"लिंकचा समावेश करा"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"स्क्रीन रेकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रेकॉर्डिंग प्रोसेस सुरू"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रेकॉर्ड सत्रासाठी सुरू असलेली सूचना"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"स्क्रीन सेव्हर"</string>
<string name="ethernet_label" msgid="2203544727007463351">"इथरनेट"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"व्यत्यय आणू नका"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"प्राधान्य मोड"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ब्लूटूथ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"कोणतेही जोडलेले डिव्हाइसेस उपलब्ध नाहीत"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"डिव्हाइस कनेक्ट किंवा डिस्कनेक्ट करण्यासाठी टॅप करा"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ वापरा"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट केले"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ऑडिओ शेअरिंग"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"व्हिडिओवर स्विच करण्यासाठी टॅप करा किंवा शेअर करा"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव्ह केले"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट करा"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ॲक्टिव्हेट करा"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिंग्ज उघडा"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"इतर डिव्हाइस"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"अवलोकन टॉगल करा."</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"प्राधान्य मोड"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"पूर्ण झाले"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिंग्ज"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"सुरू आहे"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"रेकॉर्ड किंवा कास्ट करत असताना, हे कार्य पुरवणाऱ्या सेवेला तुमच्या स्क्रीनवर दाखवलेल्या किंवा डिव्हाइसवर प्ले केलेल्या सर्व माहितीचा अॅक्सेस असेल. यामध्ये पासवर्ड, पेमेंट तपशील, फोटो, मेसेज आणि तुम्ही प्ले करत असलेला ऑडिओ यासारख्या माहितीचा समावेश आहे."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"अॅप शेअर किंवा रेकॉर्ड करा"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"तुमची स्क्रीन <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> सह शेअर करायची आहे का?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"एक अॅप शेअर करा"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"संपूर्ण स्क्रीन शेअर करा"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"एक अॅप शेअर करा"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"संपूर्ण स्क्रीन शेअर करा"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"तुम्ही तुमची संपूर्ण स्क्रीन कास्ट करता, तेव्हा तुमच्या स्क्रीनवरील कोणत्याही गोष्टी <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> साठी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"तुम्ही अॅप शेअर करता, तेव्हा त्या अॅपमध्ये दाखवल्या किंवा प्ले होणाऱ्या कोणत्याही गोष्टी <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> साठी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रीन शेअर करा"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"सॅटेलाइट, चांगले कनेक्शन"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"सॅटेलाइट, कनेक्शन उपलब्ध"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"सॅटेलाइट SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"आणीबाणी कॉल किंवा SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाईल"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"सर्वांसाठी नाही तर काहींसाठी मजेदार असू शकते"</string>
<string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनर आपल्याला Android यूझर इंटरफेस ट्विक आणि कस्टमाइझ करण्याचे अनेक प्रकार देते. ही प्रयोगात्मक वैशिष्ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्यातील रिलीज मध्ये कदाचित दिसणार नाहीत. सावधगिरी बाळगून पुढे सुरू ठेवा."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"मागे जा जेश्चर"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम जेश्चर"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"अॅक्शन की"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 8385682..181b0c7 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dan apl lain yang dibuka telah mengesan tangkapan skrin ini."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Tambahkan pada nota"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Sertakan pautan"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Perakam Skrin"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses rakaman skrin"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</string>
@@ -291,7 +292,7 @@
<string name="start_dreams" msgid="9131802557946276718">"Penyelamat skrin"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Jangan Ganggu"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Mod keutamaan"</string>
+ <string name="quick_settings_modes_label" msgid="879156359479504244">"Mod"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Tiada peranti berpasangan tersedia"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ketik untuk menyambungkan atau memutuskan sambungan peranti"</string>
@@ -300,6 +301,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Disambungkan"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Perkongsian Audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Ketik untuk menukar atau berkongsi audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan sambungan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
@@ -431,7 +433,7 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buka Tetapan"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Peranti lain"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Togol Ikhtisar"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mod keutamaan"</string>
+ <string name="zen_modes_dialog_title" msgid="8854640808100096934">"Mod"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Tetapan"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Hidup"</string>
@@ -532,8 +534,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Perkhidmatan yang menyediakan fungsi ini boleh mengakses semua maklumat yang boleh dilihat pada skrin anda atau dimainkan daripada peranti anda semasa membuat rakaman atau penghantaran. Maklumat ini termasuk kata laluan, butiran pembayaran, foto, mesej dan audio yang anda mainkan."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Kongsi atau rakam apl"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Kongsi skrin anda dengan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Kongsi satu apl"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Kongsi seluruh skrin"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Kongsi satu apl"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Kongsi seluruh skrin"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Apabila anda berkongsi seluruh skrin anda, apa-apa sahaja kandungan pada skrin anda boleh dilihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Apabila anda berkongsi apl, apa-apa sahaja kandungan yang dipaparkan atau dimainkan pada apl boleh dilihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Kongsi skrin"</string>
@@ -718,8 +724,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, sambungan yang baik"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, sambungan tersedia"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via Satelit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Panggilan kecemasan atau SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil kerja"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Menarik untuk sesetengah orang tetapi bukan untuk semua"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Penala UI Sistem memberi anda cara tambahan untuk mengolah dan menyesuaikan antara muka Android. Ciri eksperimen ini boleh berubah, rosak atau hilang dalam keluaran masa hadapan. Teruskan dengan berhati-hati."</string>
@@ -1386,6 +1391,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gerak isyarat kembali"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gerak isyarat pergi ke laman utama"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Kekunci tindakan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 60c74c1..afc57ac 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> နှင့် အခြားဖွင့်ထားသော အက်ပ်များက ဤဖန်သားပြင်ဓာတ်ပုံကို တွေ့ရှိသည်။"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"မှတ်စုတွင် ထည့်ရန်"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"လင့်ခ်ထည့်သွင်းရန်"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"ဖန်သားပြင်ရိုက်ကူးစက်"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"စကရင်ရိုက်ကူးမှု အပြီးသတ်နေသည်"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"စခရင်နားချိန်ပုံ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"အီသာနက်"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"မနှောင့်ယှက်ရ"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ဦးစားပေးမုဒ်"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ဘလူးတုသ်"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ချိတ်တွဲထားသည့် ကိရိယာများ မရှိ"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"စက်ကို ချိတ်ဆက်ရန် (သို့) ချိတ်ဆက်မှုဖြုတ်ရန် တို့ပါ"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ဘလူးတုသ်သုံးရန်"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ချိတ်ဆက်ထားသည်"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"အော်ဒီယို မျှဝေခြင်း"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"အသံ ပြောင်းရန်/မျှဝေရန် တို့ပါ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"သိမ်းထားသည်"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"စသုံးရန်"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ဆက်တင်များဖွင့်ရန်"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"အခြားစက်ပစ္စည်း"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ဖွင့်၊ ပိတ် အနှစ်ချုပ်"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ဦးစားပေးမုဒ်"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ပြီးပြီ"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ဆက်တင်များ"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ဖွင့်"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ဤလုပ်ဆောင်ချက်ကို ပေးအပ်သည့် ဝန်ဆောင်မှုသည် ရုပ်သံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်မျက်နှာပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"အက်ပ် မျှဝေခြင်း (သို့) ရိုက်ကူးခြင်း"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"သင့်စခရင်ကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> နှင့် မျှဝေမလား။"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"အက်ပ်တစ်ခု မျှဝေရန်"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"စခရင်တစ်ခုလုံး မျှဝေရန်"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"အက်ပ်တစ်ခု မျှဝေရန်"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"စခရင်တစ်ခုလုံး မျှဝေရန်"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"သင့်စခရင်တစ်ခုလုံးကို မျှဝေနေချိန်တွင် စခရင်ပေါ်ရှိ အရာအားလုံးကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> က မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"အက်ပ်ကို မျှဝေနေချိန်တွင် ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> က မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"စခရင် မျှဝေရန်"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု ကောင်းသည်"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု ရနိုင်သည်"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"အရေးပေါ်ဖုန်းခေါ်ခြင်း (သို့) SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"အလုပ် ပရိုဖိုင်"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"အချို့သူများ အတွက် ပျော်စရာ ဖြစ်ပေမဲ့ အားလုံး အတွက် မဟုတ်ပါ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"စနစ် UI Tuner က သင့်အတွက် Android အသုံးပြုသူ အင်တာဖေ့စ်ကို ပြောင်းရန်နှင့် စိတ်ကြိုက်ပြုလုပ်ရန် နည်းလမ်း အပိုများကို သင့်အတွက် စီစဉ်ပေးသည်။ အနာဂတ်ဗားရှင်းများတွင် ဤစမ်းသပ်အင်္ဂါရပ်များမှာ ပြောင်းလဲ၊ ပျက်စီး သို့မဟုတ် ပျောက်ကွယ်သွားနိုင်သည်။ သတိဖြင့် ရှေ့ဆက်ပါ။"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"နောက်သို့ လက်ဟန်"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ပင်မစာမျက်နှာ လက်ဟန်"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"လုပ်ဆောင်ချက်ကီး"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 72ccd2c..d222f31 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og andre åpne apper har registrert dette skjermbildet."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Legg til i notat"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Inkluder linken"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Skjermopptak"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skjermopptaket"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Vedvarende varsel for et skjermopptak"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Skjermsparer"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ikke forstyrr"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetsmoduser"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ingen sammenkoblede enheter er tilgjengelige"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Trykk for å koble en enhet til eller fra"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bruk Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tilkoblet"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Lyddeling"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Trykk for å bytte eller dele lyd"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koble fra"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiver"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Åpne Innstillinger"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annen enhet"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå oversikten av eller på"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetsmoduser"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Ferdig"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Innstillinger"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"På"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Tjenesten som leverer denne funksjonen, får tilgang til all informasjon som vises på skjermen eller spilles av fra enheten mens du tar opp eller caster noe. Dette inkluderer informasjon som passord, betalingsopplysninger, bilder, meldinger og lyd du spiller av."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Del eller ta opp en app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vil du dele skjermen med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Del én app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Del hele skjermen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Del én app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Del hele skjermen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Når du deler hele skjermen, er alt på skjermen synlig for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Når du deler en app, er alt som vises eller spilles av i appen, synlig for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Del skjermen"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellitt – god tilkobling"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellitt – tilkobling tilgjengelig"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-alarm via satellitt"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Nødanrop eller SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Work-profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Gøy for noen – ikke for alle"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Med System UI Tuner har du flere måter å justere og tilpasse Android-brukergrensesnittet på. Disse eksperimentelle funksjonene kan endres, avbrytes eller fjernes i fremtidige utgivelser. Fortsett med forbehold."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tilbakebevegelse"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Startskjermbevegelse"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Handlingstast"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index d680645..8af52a1 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> र खुला रहेका अन्य एपहरूले यो स्क्रिनसट भेट्टाएका छन्।"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"नोटमा सेभ गर्नुहोस्"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"लिंक समावेश गर्नुहोस्"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"स्क्रिन रेकर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रिन रेकर्डिङको प्रक्रिया अघि बढाइँदै"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"कुनै स्क्रिन रेकर्ड गर्ने सत्रका लागि चलिरहेको सूचना"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"स्क्रिन सेभर"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"बाधा नपुऱ्याउनुहोस्"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"महत्त्वपूर्ण मोडहरू"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ब्लुटुथ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"जोडी उपकरणहरू उपलब्ध छैन"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"कुनै डिभाइस कनेक्ट गर्न वा डिस्कनेक्ट गर्न ट्याप गर्नुहोस्"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लुटुथ प्रयोग गर्नुहोस्"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट गरिएको छ"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"अडियो सेयरिङ"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"अडियो बदल्न वा सेयर गर्न ट्याप गर्नुहोस्"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेभ गरिएको छ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट गर्नुहोस्"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"एक्टिभेट गर्नुहोस्"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिङ खोल्नुहोस्"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"अर्को डिभाइड"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"परिदृश्य टगल गर्नुहोस्"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"महत्त्वपूर्ण मोडहरू"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"सम्पन्न भयो"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिङ"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"अन छ"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"यो फङ्सन प्रदान गर्ने सेवाले रेकर्ड वा कास्ट गर्दै गर्दा तपाईंको स्क्रिनमा देखिने सबै जानकारी अथवा तपाईंको डिभाइसबाट प्ले गरिने सबै सामग्री हेर्न तथा प्रयोग गर्न सक्छ। यसअन्तर्गत पासवर्ड, भुक्तानीसम्बन्धी विवरण, फोटो, म्यासेज र तपाईंले प्ले गर्ने अडियो जस्ता कुराहरू समावेश हुन्छन्।"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"कुनै एप सेयर वा रेकर्ड गर्नुहोस्"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"स्क्रिन <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> सँग सेयर गर्ने हो?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"एउटा एप सेयर गर्नुहोस्"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"पूरै स्क्रिन सेयर गर्नुहोस्"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"एउटा एप सेयर गर्नुहोस्"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"पूरै स्क्रिन सेयर गर्नुहोस्"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"तपाईंले पूरै स्क्रिन सेयर गरिरहेका बेला तपाईंको स्क्रिनमा देखिने सबै सामग्री <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मा देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"तपाईंले यो एप सेयर गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मा देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रिन सेयर गर्नुहोस्"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"स्याटलाइट, राम्रो कनेक्सन"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"स्याटलाइट, कनेक्सन उपलब्ध छ"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"स्याटलाइट SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"आपत्कालीन कल वा SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाइल"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"केहीका लागि रमाइलो हुन्छ तर सबैका लागि होइन"</string>
<string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनरले तपाईँलाई Android प्रयोगकर्ता इन्टरफेस कस्टम गर्न र ट्विक गर्न थप तरिकाहरू प्रदान गर्छ। यी प्रयोगात्मक सुविधाहरू भावी विमोचनमा परिवर्तन हुन, बिग्रिन वा हराउन सक्ने छन्। सावधानीपूर्वक अगाडि बढ्नुहोस्।"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ब्याक जेस्चर"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम जेस्चर"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"एक्सन की"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 21f1cfb..c1eff5f 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -16,7 +16,11 @@
NOTE: You might also want to edit: core/res/res/values-night/*.xml
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <!-- The dark background color behind the shade -->
+ <color name="shade_scrim_background_dark">@androidprv:color/system_under_surface_dark</color>
+
<!-- The color of the legacy notifications with customs backgrounds (gingerbread and lollipop.)
It's fine to override this color since at that point the shade was dark. -->
<color name="notification_legacy_background_color">@color/GM2_grey_900</color>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 6a675b8..24a4835 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> en andere geopende apps hebben dit screenshot waargenomen."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Toevoegen aan notitie"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Link opnemen"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Schermopname"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Schermopname verwerken"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Niet storen"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteitsmodi"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Geen gekoppelde apparaten beschikbaar"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tik om een apparaat te verbinden of de verbinding te verbreken"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth gebruiken"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbonden"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio delen"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tik om audio te schakelen of te delen"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"loskoppelen"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activeren"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Instellingen openen"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ander apparaat"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Overzicht aan- of uitzetten"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteitsmodi"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Instellingen"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"De service die deze functie levert, krijgt tijdens het opnemen of casten toegang tot alle informatie die zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Dit omvat informatie zoals wachtwoorden, betalingsgegevens, foto\'s, berichten en audio die je afspeelt."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"App delen of opnemen"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Je scherm delen met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Eén app delen"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Hele scherm delen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Eén app delen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Hele scherm delen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Als je het hele scherm deelt, is alles op je scherm zichtbaar voor <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Als je een app deelt, is alles dat wordt getoond of afgespeeld in die app zichtbaar voor <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Scherm delen"</string>
@@ -629,7 +637,7 @@
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Instellingen"</string>
<string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live ondertiteling"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume verlaagd naar een veiliger niveau"</string>
- <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Het hoofdtelefoonvolume is langer dan de aanbevolen tijd hoog geweest"</string>
+ <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Het koptelefoonvolume is langer dan de aanbevolen tijd hoog geweest"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Het hoofdtelefoonvolume overschrijdt de veiligheidslimiet voor deze week"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Blijven luisteren"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Volume omlaag"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliet, goede verbinding"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliet, verbinding beschikbaar"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satelliet"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Noodoproepen of SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Werkprofiel"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Leuk voor sommige gebruikers, maar niet voor iedereen"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Met Systeem-UI-tuner beschikt u over extra manieren om de Android-gebruikersinterface aan te passen. Deze experimentele functies kunnen veranderen, vastlopen of verdwijnen in toekomstige releases. Ga voorzichtig verder."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gebaar voor terug"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gebaar voor startscherm"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Actietoets"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 6e63643..67537c2 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ଏବଂ ଅନ୍ୟ ଓପନ ଆପ୍ସ ଏହି ସ୍କ୍ରିନସଟକୁ ଚିହ୍ନଟ କରିଛି।"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ନୋଟରେ ଯୋଗ କରନ୍ତୁ"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"ଲିଙ୍କକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରନ୍ତୁ"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"ସ୍କ୍ରିନ ରେକର୍ଡର"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ସ୍କ୍ରିନ ରେକର୍ଡିଂର ପ୍ରକ୍ରିୟାକରଣ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ଏକ ସ୍କ୍ରିନ୍ ରେକର୍ଡ୍ ସେସନ୍ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string>
@@ -228,7 +229,7 @@
<string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"ଫେସ ଅନଲକ ସେଟ ଅପ କରନ୍ତୁ"</string>
<string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"ଫେସ ଅନଲକ ପୁଣି ସେଟ ଅପ କରିବାକୁ ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ଫେସ ମଡେଲ ଡିଲିଟ ହୋଇଯିବ।\n\nଆପଣଙ୍କ ଫୋନକୁ ଅନଲକ କରିବା ପାଇଁ ଆପଣଙ୍କ ଫେସ ବ୍ୟବହାର କରିବାକୁ ଆପଣଙ୍କୁ ଏହି ଫିଚର ପୁଣି ସେଟ ଅପ କରିବାକୁ ହେବ।"</string>
<string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"ଫେସ ଅନଲକ ସେଟ ଅପ କରାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରିବା ପାଇଁ ସେଟିଂସକୁ ଯାଆନ୍ତୁ।"</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"ଟିପଚିହ୍ନ ସେନସର୍କୁ ଛୁଅଁନ୍ତୁ"</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"ଟିପଚିହ୍ନ ସେନସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ"</string>
<string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"ଜାରି ରଖିବାକୁ ଅନଲକ ଆଇକନ ଦବାନ୍ତୁ"</string>
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"ଫେସ ଚିହ୍ନଟ କରାଯାଇନାହିଁ। ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
<!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"ସ୍କ୍ରିନ୍ ସେଭର୍"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ଇଥରନେଟ୍"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ପ୍ରାଥମିକତା ମୋଡ"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ବ୍ଲୁଟୁଥ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ପେୟାର୍ ହୋଇଥିବା କୌଣସି ଡିଭାଇସ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ଏକ ଡିଭାଇସ କନେକ୍ଟ କିମ୍ବା ଡିସକନେକ୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"କନେକ୍ଟ କରାଯାଇଛି"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ଅଡିଓ ସେୟାରିଂ"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ଅଡିଓ ସୁଇଚ କିମ୍ବା ସେୟାର କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ସେଭ କରାଯାଇଛି"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ଚାଲୁ କରନ୍ତୁ"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ସେଟିଂସ ଖୋଲନ୍ତୁ"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ଅନ୍ୟ ଡିଭାଇସ୍"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀକୁ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ପ୍ରାଥମିକତା ମୋଡ"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ହୋଇଗଲା"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ସେଟିଂସ"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ଚାଲୁ ଅଛି"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ରେକର୍ଡ ବା କାଷ୍ଟ କରିବା ସମୟରେ ଆପଣଙ୍କ ଡିଭାଇସରୁ ପ୍ଲେ ହେଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା ସମସ୍ତ ସୂଚନାକୁ ଏହି ଫଙ୍କସନ ପ୍ରଦାନ କରୁଥିବା ସେବାର ଆକ୍ସେସ ରହିବ। ଏଥିରେ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ଫଟୋ, ମେସେଜ ଏବଂ ଆପଣ ପ୍ଲେ କରୁଥିବା ଅଡିଓ ପରି ସୂଚନା ଅନ୍ତର୍ଭୁକ୍ତ ଅଛି।"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ଏକ ଆପକୁ ସେୟାର କିମ୍ବା ରେକର୍ଡ କରନ୍ତୁ"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ସହ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ସେୟାର କରନ୍ତୁ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ଗୋଟିଏ ଆପ ସେୟାର କରନ୍ତୁ"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ସେୟାର କରନ୍ତୁ"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ଗୋଟିଏ ଆପ ସେୟାର କରନ୍ତୁ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ସେୟାର କରନ୍ତୁ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ଆପଣ ଆପଣଙ୍କ ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ସେୟାର କରିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ସବୁକିଛି <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>କୁ ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ଆପଣ ଏକ ଆପ ସେୟାର କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>କୁ ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ସ୍କ୍ରିନ ସେୟାର କରନ୍ତୁ"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ସାଟେଲାଇଟ, ଭଲ କନେକ୍ସନ"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ସାଟେଲାଇଟ, କନେକ୍ସନ ଉପଲବ୍ଧ"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"ସେଟେଲାଇଟ SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ଜରୁରୀକାଳୀନ କଲ କିମ୍ବା SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"କେତେକଙ୍କ ପାଇଁ ମଜାଦାର, କିନ୍ତୁ ସମସ୍ତଙ୍କ ପାଇଁ ନୁହେଁ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Android ୟୁଜର୍ ଇଣ୍ଟରଫେସ୍ ବଦଳାଇବାକୁ ତଥା ନିଜ ପସନ୍ଦ ଅନୁଯାୟୀ କରିବାକୁ ସିଷ୍ଟମ୍ UI ଟ୍ୟୁନର୍ ଆପଣଙ୍କୁ ଅତିରିକ୍ତ ଉପାୟ ପ୍ରଦାନ କରେ। ଏହି ପରୀକ୍ଷାମୂଳକ ସୁବିଧାମାନ ବଦଳିପାରେ, ଭାଙ୍ଗିପାରେ କିମ୍ବା ଭବିଷ୍ୟତର ରିଲିଜ୍ଗୁଡ଼ିକରେ ନଦେଖାଯାଇପାରେ। ସତର୍କତାର ସହ ଆଗକୁ ବଢ଼ନ୍ତୁ।"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ବେକ ଜେଶ୍ଚର"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ହୋମ ଜେଶ୍ଚର"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ଆକ୍ସନ କୀ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index cd7790f..1927d45 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -104,13 +104,14 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ਅਤੇ ਹੋਰ ਖੁੱਲ੍ਹੀਆਂ ਐਪਾਂ ਨੂੰ ਇਸ ਸਕ੍ਰੀਨਸ਼ਾਟ ਦਾ ਪਤਾ ਲੱਗਿਆ ਹੈ।"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ਨੋਟ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"ਲਿੰਕ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਜਾਰੀ ਹੈ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ਕਿਸੇ ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਸੈਸ਼ਨ ਲਈ ਚੱਲ ਰਹੀ ਸੂਚਨਾ"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ਕੀ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨਾ ਹੈ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ਇੱਕ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ਜਦੋਂ ਤੁਸੀਂ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ਜਦੋਂ ਤੁਸੀਂ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਨਾਲ ਹੀ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਉਸ ਐਪ ਵਿੱਚ ਦਿਖਾਈ ਜਾਂ ਚਲਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਕਰੋ"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ਰਿਕਾਰਡ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"ਸਕ੍ਰੀਨ ਸੇਵਰ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ਈਥਰਨੈਟ"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ਤਰਜੀਹ ਮੋਡ"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ਬਲੂਟੁੱਥ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ਕੋਈ ਜੋੜਾਬੱਧ ਕੀਤੀਆਂ ਡੀਵਾਈਸਾਂ ਉਪਲਬਧ ਨਹੀਂ"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ਡੀਵਾਈਸ ਨੂੰ ਕਨੈਕਟ ਜਾਂ ਡਿਸਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ਬਲੂਟੁੱਥ ਵਰਤੋ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ਕਨੈਕਟ ਹੈ"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ਆਡੀਓ ਨੂੰ ਸਵਿੱਚ ਜਾਂ ਸਾਂਝਾ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ਹੋਰ ਡੀਵਾਈਸ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ਰੂਪ-ਰੇਖਾ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ਤਰਜੀਹ ਮੋਡ"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ਹੋ ਗਿਆ"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ਸੈਟਿੰਗਾਂ"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ਚਾਲੂ"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ਇਸ ਫੰਕਸ਼ਨ ਦੇ ਸੇਵਾ ਪ੍ਰਦਾਨਕ ਕੋਲ ਬਾਕੀ ਸਾਰੀ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ ਜੋ ਕਿ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ ਜਾਂ ਰਿਕਾਰਡਿੰਗ ਜਾਂ ਕਾਸਟ ਕਰਨ ਵੇਲੇ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਜਾਂਦੀ ਹੈ। ਇਸ ਵਿੱਚ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਫ਼ੋਟੋਆਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਤੁਹਾਡੇ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਆਡੀਓ ਦੀ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੈ।"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰੋ ਜਾਂ ਰਿਕਾਰਡ ਕਰੋ"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"ਕੀ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਨਾਲ ਆਪਣੀ ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰਨੀ ਹੈ?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ਇੱਕ ਐਪ ਸਾਂਝੀ ਕਰੋ"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰੋ"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ਇੱਕ ਐਪ ਸਾਂਝੀ ਕਰੋ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰੋ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦੌਰਾਨ, ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖ ਰਹੀ ਹਰੇਕ ਚੀਜ਼ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> \'ਤੇ ਵੀ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ਕਿਸੇ ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦੌਰਾਨ, ਉਸ ਐਪ \'ਤੇ ਦਿਖ ਰਹੀ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਨੂੰ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰੋ"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਵਧੀਆ ਹੈ"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਉਪਲਬਧ ਹੈ"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"ਸੈਟੇਲਾਈਟ SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ਐਮਰਜੈਂਸੀ ਕਾਲਾਂ ਜਾਂ ਸਹਾਇਤਾ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ਕੁਝ ਵਾਸਤੇ ਤਾਂ ਮਜ਼ੇਦਾਰ ਹੈ ਲੇਕਿਨ ਸਾਰਿਆਂ ਵਾਸਤੇ ਨਹੀਂ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"ਸਿਸਟਮ UI ਟਿਊਨਰ ਤੁਹਾਨੂੰ Android ਵਰਤੋਂਕਾਰ ਇੰਟਰਫ਼ੇਸ ਤਬਦੀਲ ਕਰਨ ਅਤੇ ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਵਾਧੂ ਤਰੀਕੇ ਦਿੰਦਾ ਹੈ। ਇਹ ਪ੍ਰਯੋਗਾਤਮਿਕ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਭਵਿੱਖ ਦੀ ਰੀਲੀਜ਼ ਵਿੱਚ ਬਦਲ ਸਕਦੀਆਂ ਹਨ, ਟੁੱਟ ਸਕਦੀਆਂ ਹਨ, ਜਾਂ ਅਲੋਪ ਹੋ ਸਕਦੀਆਂ ਹਨ। ਸਾਵਧਾਨੀ ਨਾਲ ਅੱਗੇ ਵੱਧੋ।"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ਪਿੱਛੇ ਜਾਣ ਦਾ ਇਸ਼ਾਰਾ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ਹੋਮ \'ਤੇ ਜਾਣ ਦਾ ਇਸ਼ਾਰਾ"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ਕਾਰਵਾਈ ਕੁੰਜੀ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index cf88172..1b27dba 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -19,7 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4811759950673118541">"UI systemu"</string>
+ <string name="app_label" msgid="4811759950673118541">"Interfejs systemu"</string>
<string name="battery_low_title" msgid="5319680173344341779">"Włączyć Oszczędzanie baterii?"</string>
<string name="battery_low_description" msgid="3282977755476423966">"Masz już tylko <xliff:g id="PERCENTAGE">%s</xliff:g> baterii. Oszczędzanie baterii uruchamia ciemny motyw, ogranicza aktywność w tle i opóźnia powiadomienia."</string>
<string name="battery_low_intro" msgid="5148725009653088790">"Oszczędzanie baterii uruchamia ciemny motyw, ogranicza aktywność w tle i opóźnia powiadomienia."</string>
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Aplikacja <xliff:g id="APPNAME">%1$s</xliff:g> i inne aplikacje wykryły ten zrzut ekranu."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj do notatek"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Dołącz link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Nagrywanie ekranu"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Przetwarzam nagrywanie ekranu"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Stałe powiadomienie o sesji rejestrowania zawartości ekranu"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Wygaszacz ekranu"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Nie przeszkadzać"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Tryby priorytetowe"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Brak dostępnych sparowanych urządzeń"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Kliknij, aby podłączyć lub odłączyć urządzenie"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Używaj Bluetootha"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Połączone"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Udostępnianie dźwięku"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Kliknij, aby przełączyć lub udostępnić dźwięk"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"rozłącz"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktywuj"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otwórz Ustawienia"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Inne urządzenie"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Przełącz Przegląd"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Tryby priorytetowe"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotowe"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ustawienia"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Wł."</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Podczas nagrywania i przesyłania usługa udostępniająca tę funkcję będzie miała dostęp do wszystkich informacji widocznych na ekranie lub odtwarzanych na urządzeniu. Dotyczy to m.in. haseł, szczegółów płatności, zdjęć, wiadomości i odtwarzanych dźwięków."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Udostępnianie i nagrywanie za pomocą aplikacji"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Udostępnić ekran aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Udostępnij jedną aplikację"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Udostępnij cały ekran"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Udostępnij jedną aplikację"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Udostępnij cały ekran"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kiedy udostępniasz treści z całego ekranu, aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do całości obrazu z wyświetlacza. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kiedy udostępniasz obraz z aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, widoczne jest wszystko to, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Udostępnij ekran"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelita – połączenie dobre"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelita – połączenie dostępne"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelitarne połączenie alarmowe"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Połączenia alarmowe lub SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil służbowy"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Dobra zabawa, ale nie dla każdego"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Kalibrator System UI udostępnia dodatkowe sposoby dostrajania i dostosowywania interfejsu Androida. Te eksperymentalne funkcje mogą się zmienić, popsuć lub zniknąć w przyszłych wersjach. Zachowaj ostrożność."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gest przejścia wstecz"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gest przejścia na ekran główny"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Klawisz działania"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index c2c40db..d5856af 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outros apps abertos detectaram essa captura de tela."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Incluir anotação"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Incluir link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Gravador de tela"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Protetor de tela"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Não perturbe"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritários"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Não há dispositivos pareados disponíveis"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toque para conectar ou desconectar um dispositivo"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartilhamento de áudio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toque para mudar ou compartilhar o áudio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir as Configurações"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritários"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configurações"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou reproduzidas durante uma gravação ou transmissão. Isso inclui senhas, detalhes de pagamento, fotos, mensagens e áudios."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Compartilhe ou grave um app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Compartilhar a tela com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Compartilhar um app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Compartilhar a tela inteira"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartilhar um app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartilhar a tela inteira"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando você compartilha a tela inteira, tudo nela fica visível para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando você compartilha um aplicativo, todas as informações mostradas ou abertas nele ficam visíveis para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartilhar tela"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, conexão boa"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexão disponível"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satélite"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chamadas de emergência ou SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"O sintonizador System UI fornece maneiras adicionais de ajustar e personalizar a interface do usuário do Android. Esses recursos experimentais podem mudar, falhar ou desaparecer nas versões futuras. Prossiga com cuidado."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto de volta"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto de início"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de ação"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index c52354f..6589b0f 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"A app <xliff:g id="APPNAME">%1$s</xliff:g> e outras apps abertas detetaram esta captura de ecrã."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Adicionar a uma nota"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Incluir link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Gravador de ecrã"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"A processar a gravação de ecrã"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação persistente de uma sessão de gravação de ecrã"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Proteção ecrã"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Não incomodar"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos de prioridade"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Sem dispositivos sincronizados disponíveis"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toque para associar ou desassociar um dispositivo"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ligado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Partilha de áudio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toque para mudar ou partilhar o áudio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir definições"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ativar/desativar Vista geral"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos de prioridade"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluir"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Definições"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"O serviço que fornece esta função vai ter acesso a todas as informações que estiverem visíveis no ecrã ou que forem reproduzidas a partir do dispositivo durante a gravação ou a transmissão. Isto inclui informações como palavras-passe, detalhes de pagamentos, fotos, mensagens e áudio reproduzido."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Partilhe ou grave uma app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Partilhar o seu ecrã com a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Partilhar uma app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Partilhar ecrã inteiro"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Partilhar uma app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partilhar ecrã inteiro"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando está a partilhar o ecrã inteiro, tudo o que estiver no ecrã é visível para a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando está a partilhar uma app, tudo o que é mostrado ou reproduzido nessa app é visível para a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partilhar ecrã"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, boa ligação"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, ligação disponível"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satélite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chamadas de emergência ou SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"O Sintonizador da interface do sistema disponibiliza-lhe formas adicionais ajustar e personalizar a interface do utilizador do Android. Estas funcionalidades experimentais podem ser alteradas, deixar de funcionar ou desaparecer em versões futuras. Prossiga com cuidado."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto para retroceder"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto para aceder ao ecrã principal"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de ação"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index c2c40db..d5856af 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outros apps abertos detectaram essa captura de tela."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Incluir anotação"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Incluir link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Gravador de tela"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Protetor de tela"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Não perturbe"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritários"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Não há dispositivos pareados disponíveis"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toque para conectar ou desconectar um dispositivo"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartilhamento de áudio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toque para mudar ou compartilhar o áudio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir as Configurações"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritários"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configurações"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou reproduzidas durante uma gravação ou transmissão. Isso inclui senhas, detalhes de pagamento, fotos, mensagens e áudios."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Compartilhe ou grave um app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Compartilhar a tela com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Compartilhar um app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Compartilhar a tela inteira"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartilhar um app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartilhar a tela inteira"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando você compartilha a tela inteira, tudo nela fica visível para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando você compartilha um aplicativo, todas as informações mostradas ou abertas nele ficam visíveis para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartilhar tela"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, conexão boa"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexão disponível"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satélite"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chamadas de emergência ou SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"O sintonizador System UI fornece maneiras adicionais de ajustar e personalizar a interface do usuário do Android. Esses recursos experimentais podem mudar, falhar ou desaparecer nas versões futuras. Prossiga com cuidado."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto de volta"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto de início"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de ação"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 1f7b7164..59136e0 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> și alte aplicații deschise au detectat această captură de ecran."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Adaugă în notă"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Include linkul"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Recorder pentru ecran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Se procesează înregistrarea"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificare în curs pentru o sesiune de înregistrare a ecranului"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Nu deranja"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Moduri cu prioritate"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Niciun dispozitiv conectat disponibil"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Atinge pentru a conecta sau deconecta un dispozitiv"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Folosește Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectat"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Permiterea accesului la audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Atinge pentru a comuta sau a permite accesul la conținutul audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deconectează"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activează"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Deschide Setări"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Alt dispozitiv"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comută secțiunea Recente"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Moduri cu prioritate"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gata"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Setări"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Serviciul care oferă această funcție va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrezi sau proiectezi. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redai."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Permite accesul la o aplicație sau înregistreaz-o"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Permiți accesul <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> la ecran?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Permite accesul la o aplicație"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Permite accesul la tot ecranul"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Permite accesul la o aplicație"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Permite accesul la tot ecranul"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Când permiți accesul la tot ecranul, tot conținutul de pe ecran este vizibil pentru <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Când permiți accesul la o aplicație, orice conținut se afișează sau se redă în aplicație este vizibil pentru <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Permite accesul la ecran"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, conexiune bună"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, conexiune disponibilă"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS prin satelit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Apeluri de urgență sau SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil de serviciu"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Distractiv pentru unii, dar nu pentru toată lumea"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner oferă modalități suplimentare de a ajusta și a personaliza interfața de utilizare Android. Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuă cu prudență."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gestul Înapoi"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gestul Ecran de pornire"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tastă de acțiuni"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 0b545ce..de43676 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Приложение \"<xliff:g id="APPNAME">%1$s</xliff:g>\" и другие запущенные продукты обнаружили создание скриншота."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Добавить в заметку"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Добавить ссылку"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Запись видео с экрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обработка записи с экрана…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущее уведомление для записи видео с экрана"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Заставка"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не беспокоить"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Режимы приоритета"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Нет доступных сопряженных устройств"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Нажмите, чтобы подключить или отключить устройство."</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Использовать"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Подключено"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Отправка аудио"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Нажмите, чтобы переключить аудио или поделиться им"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сохранено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"отключить"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активировать"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Открыть настройки"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Другое устройство"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Переключить режим обзора"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Режимы приоритета"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Настройки"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Включено"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Во время записи или трансляции у сервиса, предоставляющего эту функцию, будет доступ ко всему, что видно или воспроизводится на устройстве, включая пароли, сведения о способах оплаты, фотографии, сообщения и аудио."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Демонстрация или запись экрана приложения"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Показать экран приложению \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Показать приложение"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Показать весь экран"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Показать приложение"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Показать весь экран"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"При показе экрана целиком все, что на нем происходит, будет видно в приложении \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"При показе приложения все, что в нем происходит, будет видно в приложении \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Показать экран"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спутниковая связь, хорошее качество соединения"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Доступно соединение по спутниковой связи"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Спутниковый SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Экстренные вызовы или спутниковый SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Рабочий профиль"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Внимание!"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner позволяет настраивать интерфейс устройства Android по вашему вкусу. В будущем эта экспериментальная функция может измениться, перестать работать или исчезнуть."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест \"назад\""</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест \"на главный экран\""</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Клавиша действия"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index b34b8a3..8d6cd09 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> සහ අනෙකුත් විවෘත යෙදුම් මෙම තිර රුව අනාවරණය කර ගෙන ඇත."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"සටහනට එක් කරන්න"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"සබැඳිය ඇතුළත් කරන්න"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"තිර රෙකෝඩරය"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"තිර පටිගත කිරීම සකසමින්"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"තිර පටිගත කිරීමේ සැසියක් සඳහා කෙරෙන දැනුම් දීම"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"තිර සුරැකුම"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ඊතර නෙට්"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"බාධා නොකරන්න"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ප්රමුඛතා ප්රකාර"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"බ්ලූටූත්"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"යුගල කළ උපාංග නොතිබේ"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"උපාංගයක් සම්බන්ධ කිරීමට හෝ විසන්ධි කිරීමට තට්ටු කරන්න"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"බ්ලූටූත් භාවිතා කරන්න"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"සම්බන්ධිතයි"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ශ්රව්ය බෙදා ගැනීම"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ශ්රව්ය මාරු කිරීමට හෝ බෙදා ගැනීමට තට්ටු කරන්න"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"සුරැකිණි"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"විසන්ධි කරන්න"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"සක්රිය කරන්න"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"සැකසීම් විවෘත කරන්න"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"වෙනත් උපාංගය"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"දළ විශ්ලේෂණය ටොගල කරන්න"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ප්රමුඛතා ප්රකාර"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"නිමයි"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"සැකසීම්"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ක්රියාත්මකයි"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"මෙම කාර්යය සපයන සේවාවට තිරයේ පෙනෙන හෝ පටිගත කිරීමේ දී හෝ විකාශනය කිරීමේ දී ඔබේ උපාංගයේ වාදනය වන සියලු තොරතුරු වෙත ප්රවේශය ඇත. මෙයට මුරපද, ගෙවීම් විස්තර, ඡායාරූප, පණිවුඩ, සහ ඔබ වාදනය කරන ශ්රව්ය වැනි තොරතුරු ඇතුළත් වේ."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"යෙදුමක් බෙදා ගන්න හෝ පටිගත කරන්න"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> සමග ඔබේ තිරය බෙදා ගන්න ද?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"එක් යෙදුමක් බෙදා ගන්න"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"සම්පූර්ණ තිරය බෙදා ගන්න"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"එක් යෙදුමක් බෙදා ගන්න"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"සම්පූර්ණ තිරය බෙදා ගන්න"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ඔබ ඔබේ සම්පූර්ණ තිරය බෙදා ගන්නා විට, ඔබේ තිරයේ ඇති ඕනෑම දෙයක් <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> වෙත දෘශ්යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවිඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ඔබ යෙදුමක් බෙදා ගන්නා විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> වෙත දෘශ්යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවිඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"තිරය බෙදා ගන්න"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"චන්ද්රිකාව, හොඳ සම්බන්ධතාවයක්"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"චන්ද්රිකාව, සම්බන්ධතාවය තිබේ"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"චන්ද්රිකා SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"හදිසි ඇමතුම් හෝ SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"කාර්යාල පැතිකඩ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"සමහරක් දේවල් වලට විනෝදයි, නමුත් සියල්ලටම නොවේ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"පද්ධති UI සුසරකය ඔබට Android පරිශීලක අතුරු මුහුණත වෙනස් කිරීමට හෝ අභිරුචිකරණය කිරීමට අමතර ක්රම ලබා දේ. මෙම පර්යේෂණාත්මක අංග ඉදිරි නිකුත් වීම් වල වෙනස් වීමට, වැඩ නොකිරීමට, හෝ නැතිවීමට හැක. ප්රවේශමෙන් ඉදිරියට යන්න."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ආපසු අභිනය"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"නිවෙස් අභිනය"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ක්රියා යතුර"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index b1983b8..7159285 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> a ďalšie otvorené aplikácie zaznamenali túto snímku obrazovky."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Pridať do poznámky"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Zahrnúť odkaz"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Rekordér obrazovky"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Spracúva sa záznam obrazovky"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Zobrazuje sa upozornenie týkajúce sa relácie záznamu obrazovky"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Šetrič obrazovky"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Režim bez vyrušení"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Režimy priority"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nie sú k dispozícii žiadne spárované zariadenia"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Klepnutím pripojíte alebo odpojíte zariadenie"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Používať Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Pripojené"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Zdieľanie zvuku"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Klepnutím prepnete alebo budete zdieľať zvuk"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojiť"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovať"</string>
@@ -389,7 +392,7 @@
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string>
<string name="performance" msgid="6552785217174378320">"Výkon"</string>
<string name="user_interface" msgid="3712869377953950887">"Používateľské rozhranie"</string>
- <string name="thermal" msgid="6758074791325414831">"Termálne"</string>
+ <string name="thermal" msgid="6758074791325414831">"Teplota"</string>
<string name="custom" msgid="3337456985275158299">"Vlastné"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Nastavenia vlastnej stopy"</string>
<string name="restore_default" msgid="5259420807486239755">"Obnoviť predvolené"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvoriť Nastavenia"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Iné zariadenie"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Prepnúť prehľad"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Režimy priority"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavenia"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Zapnuté"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Služba poskytujúca túto funkciu bude mať prístup k všetkým informáciám zobrazovaným na obrazovke alebo prehrávaným v zariadení počas nahrávania či prenosu. Patria medzi ne informácie, ako sú heslá, platobné údaje, fotky, správy a prehrávaný zvuk."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Aplikácia na zdieľanie alebo nahrávanie"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Chcete zdieľať obrazovku s aplikáciou <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Zdieľať jednu aplikáciu"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Zdieľať celú obrazovku"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Zdieľať jednu aplikáciu"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Zdieľať celú obrazovku"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pri zdieľaní celej obrazovky vidí aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> všetko, čo sa na nej zobrazuje. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Pri zdieľaní aplikácie vidí aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> všetko, čo sa v zdieľanej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Zdieľať obrazovku"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobrá kvalita pripojenia"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, pripojenie je k dispozícii"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Pomoc cez satelit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Tiesňové volania alebo pomoc v tiesni"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Pracovný profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Pri používaní tuneru postupujte opatrne"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Tuner používateľského rozhrania systému poskytujte ďalšie spôsoby ladenia a prispôsobenia používateľského rozhrania Android. Tieto experimentálne funkcie sa môžu v budúcich verziách zmeniť, ich poskytovanie môže byť prerušené alebo môžu byť odstránené. Pokračujte opatrne."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto prechodu späť"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto prechodu domov"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Akčný kláves"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index a3b042e..f2c466e 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> in druge odprte aplikacije so zaznale ta posnetek zaslona."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj v zapisek"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Vključi povezavo"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Snemalnik zaslona"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obdelava videoposnetka zaslona"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Nenehno obveščanje o seji snemanja zaslona"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Ohranjeval. zaslona"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne moti"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prednostni načini"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Na voljo ni nobene seznanjene naprave"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dotaknite se za vzpostavitev ali prekinitev povezave z napravo"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Uporabi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Deljenje zvoka"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dotaknite se za preklop ali deljenje zvoka"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinitev povezave"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Odpri nastavitve"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Druga naprava"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Vklop/izklop pregleda"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prednostni načini"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Končano"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavitve"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Vklopljeno"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Storitev, ki zagotavlja to funkcijo, bo imela dostop do vseh podatkov, ki so med snemanjem ali predvajanjem prikazani na vašem zaslonu ali se predvajajo iz vaše naprave. To vključuje podatke, kot so gesla, podrobnosti o plačilu, fotografije, sporočila in zvok, ki ga predvajate."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Deljenje ali snemanje aplikacije"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Želite deliti zaslon z aplikacijo <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Deli eno aplikacijo"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Deli celoten zaslon"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Deli eno aplikacijo"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deli celoten zaslon"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pri deljenju celotnega zaslona je aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidno vse na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Pri deljenju aplikacije je aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidno vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deli zaslon"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra povezava"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, povezava je na voljo"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS prek satelita"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Klici v sili ali SOS prek satelita"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Delovni profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zabavno za nekatere, a ne za vse"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Uglaševalnik uporabniškega vmesnika sistema vam omogoča dodatne načine za spreminjanje in prilagajanje uporabniškega vmesnika Android. Te poskusne funkcije lahko v prihodnjih izdajah kadar koli izginejo, se spremenijo ali pokvarijo. Bodite previdni."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Poteza za pomik nazaj"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Poteza za začetni zaslon"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Gumb za dejanje"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 4cba527..70ea421 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dhe aplikacionet e tjera të hapura zbuluan këtë pamje ekrani."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Shto te shënimi"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Përfshi lidhjen"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Regjistruesi i ekranit"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Regjistrimi i ekranit po përpunohet"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Njoftim i vazhdueshëm për një seancë regjistrimi të ekranit"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Mbrojtësi i ekranit"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Eternet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Mos shqetëso"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modalitetet e përparësisë"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth-i"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nuk ofrohet për përdorim asnjë pajisje e çiftuar"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Trokit për të lidhur ose shkëputur një pajisje"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Përdor Bluetooth-in"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Lidhur"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ndarja e audios"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Trokit për të ndërruar ose ndarë audion"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Hap \"Cilësimet\""</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Pajisje tjetër"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kalo te përmbledhja"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modalitetet e përparësisë"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"U krye"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Cilësimet"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Shërbimi që e ofron këtë funksion do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione, si p.sh.: fjalëkalimet, detajet e pagesave, fotografitë, mesazhet, si dhe audion që luan ti."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Ndaj ose regjistro një aplikacion"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Të ndahet ekrani yt me <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Ndaj një aplikacion"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Ndaj të gjithë ekranin"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Ndaj një aplikacion"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ndaj të gjithë ekranin"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kur ti ndan të gjithë ekranin, çdo gjë në ekranin tënd është e dukshme për <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kur ti ndan një aplikacion, çdo gjë që shfaqet ose luhet në atë aplikacion është e dukshme për <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ndaj ekranin"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Sateliti. Lidhje e mirë"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Sateliti. Ofrohet lidhje"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS satelitor"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Telefonatat e urgjencës ose SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profili i punës"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Argëtim për disa, por jo për të gjithë!"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit të jep mënyra shtesë për të tërhequr dhe personalizuar ndërfaqen Android të përdoruesit. Këto funksione eksperimentale mund të ndryshojnë, prishen ose zhduken në versionet e ardhshme. Vazhdo me kujdes."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gjesti i kthimit prapa"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gjesti për të shkuar tek ekrani bazë"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tasti i veprimit"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 26c135b..6db372d 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> и друге отворене апликације су откриле овај снимак екрана."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Додај у белешку"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Уврсти линк"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Снимач екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обрађујемо видео снимка екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Обавештење о сесији снимања екрана је активно"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Чувар екрана"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Етернет"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не узнемиравај"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Није доступан ниједан упарени уређај"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Додирните да бисте повезали уређај или прекинули везу"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Повезано"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Дељење звука"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Додирните да бисте пребацили или делили звук"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сачувано"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекините везу"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирајте"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Отвори Подешавања"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Други уређај"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Укључи/искључи преглед"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Подешавања"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Укључено"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Услуга која пружа ову функцију ће имати приступ свим информацијама које се приказују на екрану или репродукују са уређаја током снимања или пребацивања. То обухвата информације попут лозинки, информација о плаћању, слика, порука и звука који пуштате."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Делите или снимите апликацију"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Желите да делите екран са апликацијом <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Дели једну апликацију"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Дели цео екран"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Дели једну апликацију"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Дели цео екран"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Када делите цео екран, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> види све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Када делите апликацију, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> види сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Дели екран"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Сателит, веза је добра"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Сателит, веза је доступна"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Хитна помоћ преко сателита"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Хитни позиви или хитна помоћ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Пословни профил"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Забава за неке, али не за све"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Тјунер за кориснички интерфејс система вам пружа додатне начине за подешавање и прилагођавање Android корисничког интерфејса. Ове експерименталне функције могу да се промене, откажу или нестану у будућим издањима. Будите опрезни."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Покрет за враћање"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Покрет за почетну страницу"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Тастер радњи"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 56cc442..225e3b3 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> och andra öppna appar identifierade skärmbilden."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Lägg till i anteckning"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Inkludera länk"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Skärminspelare"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandlar skärminspelning"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Avisering om att skärminspelning pågår"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Skärmsläckare"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Stör ej"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriterade lägen"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Det finns inga kopplade enheter tillgängliga"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tryck för att ansluta eller koppla från en enhet"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Använd Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ansluten"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ljuddelning"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tryck för att byta eller dela ljud"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koppla från"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivera"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Öppna Inställningar"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annan enhet"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktivera och inaktivera översikten"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriterade lägen"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klar"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Inställningar"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"På"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Den tjänst som tillhandahåller funktionen får åtkomst till all information som visas på skärmen eller spelas upp från enheten när du spelar in eller castar. Detta omfattar till exempel lösenord, betalningsuppgifter, foton, meddelanden och ljud som du spelar upp."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Dela eller spela in en app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vill du dela skärmen med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Dela en app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Dela hela skärmen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Dela en app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dela hela skärmen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"När du delar hela skärmen är allt på skärmen synligt för <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"När du delar en app är allt som visas eller spelas upp i appen synligt för <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dela skärmen"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit, bra anslutning"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit, anslutning tillgänglig"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-larm via satellit"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Nödsamtal eller SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Jobbprofil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Kul för vissa, inte för alla"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Du kan använda inställningarna för systemgränssnitt för att justera användargränssnittet i Android. Dessa experimentfunktioner kan när som helst ändras, sluta fungera eller försvinna. Använd med försiktighet."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tillbaka-rörelse"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Rörelse för att öppna startskärmen"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Åtgärdstangent"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index db237b5..9fa8e55 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> na zingine zinazotumika zimetambua picha hii ya skrini."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ongeza kwenye dokezo"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Jumuisha kiungo"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g> <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Kinasa Skrini"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string>
@@ -124,7 +125,7 @@
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Inarekodi skrini na sauti"</string>
<string name="screenrecord_taps_label" msgid="1595690528298857649">"Onyesha sehemu za kugusa kwenye skrini"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Acha"</string>
- <string name="screenrecord_share_label" msgid="5025590804030086930">"Shiriki"</string>
+ <string name="screenrecord_share_label" msgid="5025590804030086930">"Tuma"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"Imehifadhi rekodi ya skrini"</string>
<string name="screenrecord_save_text" msgid="3008973099800840163">"Gusa ili uangalie"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Hitilafu imetokea wakati wa kuhifadhi rekodi ya skrini"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Taswira ya skrini"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Usinisumbue"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Hali za kipaumbele"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Hakuna vifaa vilivyooanishwa vinavyopatikana"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Gusa ili uunganishe au utenganishe kifaa"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Tumia Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Imeunganishwa"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Kusikiliza Pamoja"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Gusa ili ubadilishe sauti au usikilize pamoja na wengine"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ondoa"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"anza kutumia"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Fungua Mipangilio"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Kifaa kingine"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Washa Muhtasari"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Hali za kipaumbele"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Nimemaliza"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Mipangilio"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Imewashwa"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Huduma inayotoa utendaji huu itaweza kufikia maelezo yote yanayoonekana kwenye skrini yako au yanayochezwa kwenye kifaa chako wakati wa kurekodi au kutuma. Hii ni pamoja na maelezo kama vile manenosiri, maelezo ya malipo, picha, ujumbe na sauti unayocheza."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Kurekodi au kuruhusu programu ifikiwe"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ungependa kuruhusu <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ifikie skrini yako?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Ruhusu ufikiaji wa programu moja"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Ruhusu ufikiaji wa skrini nzima"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Ruhusu ufikiaji wa programu moja"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ruhusu ufikiaji wa skrini nzima"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Unaporuhusu ufikiaji wa skrini nzima, chochote kilicho katika skrini yako kitaonekana kwa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Unaporuhusu ufikiaji wa programu, chochote kinachoonyeshwa au kuchezwa katika programu hiyo kitaonekana kwa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ruhusu ufikiaji wa skrini"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Setilaiti, muunganisho thabiti"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Setilaiti, muunganisho unapatikana"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Msaada kupitia Setilaiti"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Simu za dharura"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Wasifu wa kazini"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Kinafurahisha kwa baadhi ya watu lakini si wote"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Kirekebishi cha kiolesura cha mfumo kinakupa njia zaidi za kugeuza na kubadilisha kiolesura cha Android ili kikufae. Vipengele hivi vya majaribio vinaweza kubadilika, kuharibika au kupotea katika matoleo ya siku zijazo. Endelea kwa uangalifu."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Ishara ya kurudi nyuma"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Mguso wa kurudi kwenye skrini ya kwanza"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Kitufe cha vitendo"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index e617e88..7acd1e8 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"இந்த ஸ்கிரீன்ஷாட்டை <xliff:g id="APPNAME">%1$s</xliff:g> மற்றும் திறந்திருக்கும் பிற ஆப்ஸ் கண்டறிந்துள்ளன."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"குறிப்பில் சேர்"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"இணைப்பைச் சேர்"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"ஸ்கிரீன் ரெக்கார்டர்"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ஸ்க்ரீன் ரெக்கார்டிங் செயலாக்கப்படுகிறது"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"திரை ரெக்கார்டிங் அமர்விற்கான தொடர் அறிவிப்பு"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"ஸ்கிரீன் சேவர்"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ஈதர்நெட்"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"தொந்தரவு செய்ய வேண்டாம்"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"முன்னுரிமைப் பயன்முறைகள்"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"புளூடூத்"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"இணைக்கப்பட்ட சாதனங்கள் இல்லை"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"சாதனத்தை இணைக்க/துண்டிக்க தட்டவும்"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"புளூடூத்தைப் பயன்படுத்துதல்"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"இணைக்கப்பட்டது"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ஆடியோ பகிர்வு"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ஆடியோவை மாற்ற அல்லது பகிர, தட்டவும்"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"இணைப்பு நீக்கும்"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"செயல்படுத்தும்"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"அமைப்புகளைத் திற"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"பிற சாதனம்"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"மேலோட்டப் பார்வையை நிலைமாற்று"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"முன்னுரிமைப் பயன்முறைகள்"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"முடிந்தது"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"அமைப்புகள்"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"இயக்கப்பட்டுள்ளது"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ உங்கள் திரையில் காட்டப்படுகின்ற அல்லது சாதனத்திலிருந்து பிளே செய்யப்படுகின்ற அனைத்துத் தகவல்களையும் இந்தச் செயல்பாட்டை வழங்கும் சேவையால் அணுக முடியும். கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், படங்கள், மெசேஜ்கள், நீங்கள் பிளே செய்யும் ஆடியோ போன்றவை இதிலடங்கும்."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ஆப்ஸைப் பகிர்தல் அல்லது ரெக்கார்டு செய்தல்"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> உடன் திரையைப் பகிரவா?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ஓர் ஆப்ஸைப் பகிர்"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"முழுத் திரையையும் பகிர்"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ஓர் ஆப்ஸைப் பகிர்"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"முழுத் திரையையும் பகிர்"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"உங்கள் முழுத்திரையையும் பகிரும்போது, திரையில் உள்ள அனைத்தும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> இல் தெரியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ஓர் ஆப்ஸைப் பகிரும்போது, அதில் காட்டப்படும்/பிளே செய்யப்படும் அனைத்தும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> இல் தெரியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"திரையைப் பகிர்"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"சாட்டிலைட், நிலையான இணைப்பு"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"சாட்டிலைட், இணைப்பு கிடைக்கிறது"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"சாட்டிலைட் SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"அவசர அழைப்புகள் அல்லது SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"பணிக் கணக்கு"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"சில வேடிக்கையாக இருந்தாலும் கவனம் தேவை"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner, Android பயனர் இடைமுகத்தை மாற்றவும் தனிப்பயனாக்கவும் கூடுதல் வழிகளை வழங்குகிறது. இந்தப் பரிசோதனைக்குரிய அம்சங்கள் எதிர்கால வெளியீடுகளில் மாற்றப்படலாம், இடைநிறுத்தப்படலாம் அல்லது தோன்றாமல் போகலாம். கவனத்துடன் தொடரவும்."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"பின்செல்வதற்கான சைகை"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"முகப்பிற்குச் செல்வதற்கான சைகை"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ஆக்ஷன் பட்டன்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index b45e82b..5cba2ef 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g>, ఇతర ఓపెన్ యాప్లు ఈ స్క్రీన్షాట్ను గుర్తించాయి."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"గమనికకు జోడించండి"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"లింక్ను చేర్చండి"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"స్క్రీన్ రికార్డర్"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"స్క్రీన్ రికార్డింగ్ అవుతోంది"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"స్క్రీన్ రికార్డ్ సెషన్ కోసం ఆన్గోయింగ్ నోటిఫికేషన్"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"స్క్రీన్ సేవర్"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ఈథర్నెట్"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"అంతరాయం కలిగించవద్దు"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ముఖ్యమైన ఫైల్స్ మోడ్స్"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"బ్లూటూత్"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"జత చేసిన పరికరాలు ఏవీ అందుబాటులో లేవు"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"పరికరాన్ని కనెక్ట్ చేయడానికి లేదా డిస్కనెక్ట్ చేయడానికి ట్యాప్ చేయండి"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"బ్లూటూత్ వాడండి"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"కనెక్ట్ అయింది"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ఆడియో షేరింగ్"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ఆడియోను మార్చడానికి లేదా షేర్ చేయడానికి ట్యాప్ చేయండి"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"సేవ్ అయ్యింది"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"డిస్కనెక్ట్ చేయండి"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"యాక్టివేట్ చేయండి"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"సెట్టింగ్లను తెరవండి"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ఇతర పరికరం"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ముఖ్యమైన ఫైళ్ల మోడ్స్"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"పూర్తయింది"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"సెట్టింగ్లు"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"ఆన్లో ఉంది"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు మీ స్క్రీన్పై చూపబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, ఈ ఫంక్షన్ను అందిస్తున్న సర్వీస్ యాక్సెస్ చేయగలదు. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"యాప్ను షేర్ చేయండి లేదా రికార్డ్ చేయండి"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"మీ స్క్రీన్ను <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>తో షేర్ చేయండి?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ఒక యాప్ను షేర్ చేయండి"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"మొత్తం స్క్రీన్ను షేర్ చేయండి"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ఒక యాప్ను షేర్ చేయండి"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"మొత్తం స్క్రీన్ను షేర్ చేయండి"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"మీ మొత్తం స్క్రీన్ను మీరు షేర్ చేసేటప్పుడు, మీ స్క్రీన్పై ఉన్నవన్నీ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>కు కనిపిస్తాయి. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"మీరు యాప్ను షేర్ చేసేటప్పుడు, సంబంధిత యాప్లో కనిపించేవి లేదా ప్లే అయ్యేవన్నీ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>కు కనిపిస్తాయి. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"స్క్రీన్ను షేర్ చేయండి"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"శాటిలైట్, కనెక్షన్ బాగుంది"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"శాటిలైట్, కనెక్షన్ అందుబాటులో ఉంది"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"ఎమర్జెన్సీ శాటిలైట్ సహాయం"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ఎమర్జెన్సీ కాల్స్ లేదా SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ఆఫీస్ ప్రొఫైల్"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"కొందరికి సరదాగా ఉంటుంది కానీ అందరికీ అలాగే ఉండదు"</string>
<string name="tuner_warning" msgid="1861736288458481650">"సిస్టమ్ UI ట్యూనర్ Android వినియోగదారు ఇంటర్ఫేస్ను మెరుగుపరచడానికి మరియు అనుకూలంగా మార్చడానికి మీకు మరిన్ని మార్గాలను అందిస్తుంది. ఈ ప్రయోగాత్మక లక్షణాలు భవిష్యత్తు విడుదలల్లో మార్పుకు లోనవ్వచ్చు, తాత్కాలికంగా లేదా పూర్తిగా నిలిపివేయవచ్చు. జాగ్రత్తగా కొనసాగండి."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"వెనుకకు పంపే సంజ్ఞ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"హోమ్కు పంపే సంజ్ఞ"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"యాక్షన్ కీ"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 252fc05..905ab98 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> และแอปอื่นๆ ที่เปิดอยู่ตรวจพบภาพหน้าจอนี้"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"เพิ่มลงในโน้ต"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"รวมลิงก์"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"โปรแกรมบันทึกหน้าจอ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"ภาพพักหน้าจอ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"อีเทอร์เน็ต"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ห้ามรบกวน"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"โหมดสำคัญ"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"บลูทูธ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ไม่มีอุปกรณ์ที่จับคู่ที่สามารถใช้ได้"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"แตะเพื่อเชื่อมต่อหรือยกเลิกการเชื่อมต่ออุปกรณ์"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ใช้บลูทูธ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"เชื่อมต่อแล้ว"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"การแชร์เสียง"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"แตะเพื่อสลับหรือแชร์เสียง"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"บันทึกแล้ว"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"เปิดการตั้งค่า"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"อุปกรณ์อื่น"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"สลับภาพรวม"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"โหมดสำคัญ"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"เสร็จสิ้น"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"การตั้งค่า"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"เปิด"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"บริการที่มีฟังก์ชันนี้จะมีสิทธิ์เข้าถึงข้อมูลทั้งหมดที่ปรากฏบนหน้าจอหรือเปิดจากอุปกรณ์ของคุณขณะบันทึกหรือแคสต์ ซึ่งรวมถึงข้อมูลอย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน รูปภาพ ข้อความ และเสียงที่คุณเล่น"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"แชร์หรือบันทึกแอป"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"แชร์หน้าจอกับ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ไหม"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"แชร์แอปเดียว"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"แชร์ทั้งหน้าจอ"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"แชร์แอปเดียว"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"แชร์ทั้งหน้าจอ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"เมื่อกำลังแชร์ทั้งหน้าจอ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมองเห็นทุกสิ่งที่อยู่บนหน้าจอ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"เมื่อกำลังแชร์แอป <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมองเห็นทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"แชร์หน้าจอ"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ดาวเทียม, การเชื่อมต่อดี"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ดาวเทียม, การเชื่อมต่อที่พร้อมใช้งาน"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS ดาวเทียม"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"การโทรฉุกเฉินหรือ SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"โปรไฟล์งาน"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"เพลิดเพลินกับบางส่วนแต่ไม่ใช่ทั้งหมด"</string>
<string name="tuner_warning" msgid="1861736288458481650">"ตัวรับสัญญาณ UI ระบบช่วยให้คุณมีวิธีพิเศษในการปรับแต่งและกำหนดค่าส่วนติดต่อผู้ใช้ Android ฟีเจอร์รุ่นทดลองเหล่านี้อาจมีการเปลี่ยนแปลง ขัดข้อง หรือหายไปในเวอร์ชันอนาคต โปรดดำเนินการด้วยความระมัดระวัง"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ท่าทางสัมผัสสำหรับย้อนกลับ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ท่าทางสัมผัสสำหรับหน้าแรก"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ปุ่มดำเนินการ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 04f52ea..5e9d720 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Na-detect ng <xliff:g id="APPNAME">%1$s</xliff:g> at ng iba pang bukas na app ang screenshot na ito."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Idagdag sa tala"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Isama ang link"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Recorder ng Screen"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pinoproseso screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Kasalukuyang notification para sa session ng pag-record ng screen"</string>
@@ -291,7 +292,7 @@
<string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Huwag Istorbohin"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Mga mode ng priyoridad"</string>
+ <string name="quick_settings_modes_label" msgid="879156359479504244">"Mga Mode"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Walang available na mga magkapares na device"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Mag-tap para magkonekta o magdiskonekta ng device"</string>
@@ -300,6 +301,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gumamit ng Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Nakakonekta"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Pag-share ng Audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"I-tap para lumipat o magbahagi ng audio"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Na-save"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"idiskonekta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"i-activate"</string>
@@ -431,7 +433,7 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buksan ang Mga Setting"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Iba pang device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"I-toggle ang Overview"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mga priyoridad na mode"</string>
+ <string name="zen_modes_dialog_title" msgid="8854640808100096934">"Mga Mode"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tapos na"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Mga Setting"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Naka-on"</string>
@@ -532,8 +534,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Ang serbisyong nagbibigay ng function na ito ay magkakaroon ng access sa lahat ng impormasyong nakikita sa iyong screen o pine-play mula sa device mo habang nagre-record o nagka-cast. Kasama rito ang impormasyong tulad ng mga password, detalye ng pagbabayad, larawan, mensahe, at audio na pine-play mo."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Magbahagi o mag-record ng app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ibahagi ang iyong screen sa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Magbahagi ng isang app"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Ibahagi ang buong screen"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Mag-share ng isang app"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"I-share ang buong screen"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kapag ibinahagi mo ang iyong buong screen, makikita ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ang kahit anong nasa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kapag nagbabahagi ka ng app, makikita ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ang kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ibahagi ang screen"</string>
@@ -629,7 +635,7 @@
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Mga Setting"</string>
<string name="volume_panel_captioning_title" msgid="5984936949147684357">"Instant Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Ibinaba sa mas ligtas na level ang volume"</string>
- <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Naging malakas ang volume ng headphones nang mas matagal sa inirerekomenda"</string>
+ <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Malakas ang volume ng headphones nang mas matagal sa inirerekomenda"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Lampas na sa ligtas na limitasyon para sa linggong ito ang volume ng headphone"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Magpatuloy sa pakikinig"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Hinaan"</string>
@@ -718,8 +724,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, malakas ang koneksyon"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, may koneksyon"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Mga emergency na tawag o SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profile sa trabaho"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Masaya para sa ilan ngunit hindi para sa lahat"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Nagbibigay sa iyo ang Tuner ng System UI ng mga karagdagang paraan upang baguhin at i-customize ang user interface ng Android. Ang mga pang-eksperimentong feature na ito ay maaaring magbago, masira o mawala sa mga pagpapalabas sa hinaharap. Magpatuloy nang may pag-iingat."</string>
@@ -1386,6 +1391,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Galaw para bumalik"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Galaw para sa Home"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 19c9e0c..db49402 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ve diğer açık uygulamalar bu ekran görüntüsünü algıladı."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Nota ekle"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Bağlantıyı dahil et"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Ekran Kaydedicisi"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran kaydı işleniyor"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekran kaydı oturumu için devam eden bildirim"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Ekran koruyucu"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Rahatsız Etmeyin"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Öncelik modları"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Kullanılabilir eşlenmiş cihaz yok"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Cihaz bağlamak veya cihazın bağlantısını kesmek için dokunun"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth\'u kullan"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Bağlandı"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ses Paylaşımı"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Geçiş yapmak veya ses paylaşmak için dokunun"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"bağlantıyı kes"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"etkinleştir"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ayarlar\'ı aç"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Diğer cihaz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Genel bakışı aç/kapat"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Öncelik modları"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Bitti"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ayarlar"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Açık"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Bu işlevi sağlayan hizmet, ekranınızda görünen veya kayıt ya da yayın sırasında cihazınızdan oynatılan tüm bilgilere erişecektir. Bu bilgiler arasında şifreler, ödeme detayları, fotoğraflar, mesajlar ve çaldığınız sesler gibi bilgiler yer alır."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Uygulamayı paylaşın veya kaydedin"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ekranınız <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> uygulamasıyla paylaşılsın mı?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Tek bir uygulamayı paylaş"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Tüm ekranı paylaş"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Tek bir uygulamayı paylaş"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Tüm ekranı paylaş"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, tüm ekranınızı paylaştığınızda ekranınızdaki her şeyi görebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, bir uygulamayı paylaştığınızda o uygulamada gösterilen veya oynatılan her şeyi görebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranı paylaş"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Uydu, bağlantı güçlü"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Uydu, bağlantı mevcut"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Acil Uydu Bağlantısı"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Acil durum aramaları veya acil yardım"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"İş profili"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Bazıları için eğlenceliyken diğerleri için olmayabilir"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sistem Kullanıcı Arayüzü Ayarlayıcı, Android kullanıcı arayüzünde değişiklikler yapmanız ve arayüzü özelleştirmeniz için ekstra yollar sağlar. Bu deneysel özellikler değişebilir, bozulabilir veya gelecekteki sürümlerde yer almayabilir. Dikkatli bir şekilde devam edin."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geri hareketi"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Ana sayfa hareketi"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Eylem tuşu"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 8af4120..6d881df 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> та інші відкриті додатки виявили цей знімок екрана."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Додати до примітки"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Додати посилання"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Запис відео з екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обробка записування екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Сповіщення про сеанс запису екрана"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Заставка"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не турбувати"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Режими пріоритету"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Немає спарених пристроїв"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Натисніть, щоб під’єднати або від’єднати пристрій"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Увімкнути Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Підключено"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Надсилання аудіо"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Натисніть, щоб змінити режим або надіслати аудіо"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Збережено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"від’єднати"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активувати"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Відкрити налаштування"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Інший пристрій"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Увімкнути або вимкнути огляд"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Режими пріоритету"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Налаштування"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Увімкнено"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Сервіс, що надає цю функцію, матиме доступ до всієї інформації, яка з’являється на екрані або відтворюється на пристрої під час запису чи трансляції, зокрема до паролів, платіжної інформації, фотографій, повідомлень і аудіофайлів."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Показувати або записувати додаток"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Показати екран для додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Показати один додаток"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Показати весь екран"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Показати один додаток"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Показати весь екран"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Коли ви показуєте весь екран, для додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> стає видимим увесь контент на ньому. Тому будьте обережні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Коли ви показуєте додаток, для додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> стає видимим увесь контент, що відображається або відтворюється в ньому. Тому будьте обережні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Показати екран"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Хороше з’єднання із супутником"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Доступне з’єднання із супутником"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Супутниковий сигнал SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Екстрені виклики або сигнал SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Робочий профіль"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Це цікаво, але будьте обачні"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner пропонує нові способи налаштувати та персоналізувати інтерфейс користувача Android. Ці експериментальні функції можуть змінюватися, не працювати чи зникати в майбутніх версіях. Будьте обачні."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест \"Назад\""</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест переходу на головний екран"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Клавіша дії"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index dd27515..628d660 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> اور دیگر کھلی ایپس نے اس اسکرین شاٹ کا پتا لگایا۔"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"نوٹ میں شامل کریں"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"لنک شامل کریں"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"اسکرین ریکارڈر"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"سکرین ریکارڈنگ پروسیس ہورہی ہے"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"اسکرین ریکارڈ سیشن کیلئے جاری اطلاع"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"اسکرین سیور"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ایتھرنیٹ"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ڈسٹرب نہ کریں"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"ترجیحی وضع"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوٹوتھ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"کوئی جوڑا بنائے ہوئے آلات دستیاب نہیں ہیں"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"کسی آلے کو منسلک یا غیر منسلک کرنے کے لیے تھپتھپائیں"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"بلوٹوتھ استعمال کریں"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"منسلک ہے"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"آڈیو کا اشتراک"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"آڈیو پر سوئچ کرنے یا اس کا اشتراک کرنے کے لیے تھپتھپائیں"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ترتیبات کھولیں"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"دوسرا آلہ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"مجموعی جائزہ ٹوگل کریں"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ترجیحی وضع"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"ہو گیا"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ترتیبات"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"آن ہے"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"اس فنکشن فراہم کرنے والی سروس کو اس تمام معلومات تک رسائی حاصل ہوگی جو آپ کی اسکرین پر نظر آتی ہے یا ریکارڈنگ یا کاسٹنگ کے دوران آپ کے آلے سے چلائی گئی ہے۔ اس میں پاس ورڈز، ادائیگی کی تفصیلات، تصاویر، پیغامات اور آپ کے ذریعے چلائی جانے والی آڈیو جیسی معلومات شامل ہے۔"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ایپ کا اشتراک یا ریکارڈ کریں"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کے ساتھ اپنی اسکرین کا اشتراک کریں؟"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"ایک ایپ کا اشتراک کریں"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"پوری اسکرین کا اشتراک کریں"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ایک ایپ کا اشتراک کریں"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"پوری اسکرین کا اشتراک کریں"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"آپ کے اپنی پوری اسکرین کا اشتراک کرنے پر آپ کی اسکرین پر ہر چیز <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کیلئے مرئی ہوجاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"آپ کے کسی ایپ کا اشتراک کرنے پر اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کیلئے مرئی ہو جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"اسکرین کا اشتراک کریں"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"سیٹلائٹ، کنکشن اچھا ہے"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"سیٹلائٹ، کنکشن دستیاب ہے"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"سیٹلائٹ SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ایمرجنسی کالز یا SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"دفتری پروفائل"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"کچھ کیلئے دلچسپ لیکن سبھی کیلئے نہیں"</string>
<string name="tuner_warning" msgid="1861736288458481650">"سسٹم UI ٹیونر Android صارف انٹر فیس میں ردوبدل کرنے اور اسے حسب ضرورت بنانے کیلئے آپ کو اضافی طریقے دیتا ہے۔ یہ تجرباتی خصوصیات مستقبل کی ریلیزز میں تبدیل ہو سکتی، رک سکتی یا غائب ہو سکتی ہیں۔ احتیاط کے ساتھ آگے بڑھیں۔"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"پیچھے جانے کا اشارہ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ہوم کا اشارہ"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ایکشن کلید"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index b0ba287..f0988ed 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> va boshqa ochiq ilovalar skrinshot olinganini aniqladi."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Qaydga qoʻshish"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Havolani kiritish"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g>, <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Ekranni yozib olish"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran yozib olinmoqda"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekrandan yozib olish seansi uchun joriy bildirishnoma"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Ekran lavhasi"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Bezovta qilinmasin"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Muhim rejimlar"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ulangan qurilmalar topilmadi"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Qurilma ulash yoki uzish uchun tegining"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ishlatish"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ulangan"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio ulashuvi"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Audioni almashtirish yoki ulashish uchun bosing"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"uzish"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"faollashtirish"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Sozlamalarni ochish"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Boshqa qurilma"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Umumiy nazar rejimini almashtirish"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Muhim rejimlar"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tayyor"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Sozlamalar"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Yoniq"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Bu funksiyani taʼminlovchi xizmat ekranda chiqqan yoki yozib olish va translatsiya vaqtida ijro etilgan barcha axborotlarga ruxsat oladi. Bu axborotlar parollar, toʻlov tafsilotlari, rasmlar, xabarlar va ijro etilgan audiolardan iborat boʻlishi mumkin."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Ilovani ulashish yoki yozib olish"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ekraningiz <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bilan ulashilsinmi?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Bitta ilovani namoyish qilish"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Butun ekranni namoyish qilish"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Bitta ilovani namoyish qilish"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Butun ekranni namoyish qilish"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Butun ekranni namoyish qilayotganingizda, ekrandagi barcha narsalaringiz <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ga koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Ilovani namoyish qilayotganingizda oʻsha ilova ichida koʻrsatilayotgan yoki ijro qilinayotganlar <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ga koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranni namoyish qilish"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Sputnik, aloqa sifati yaxshi"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Sputnik, aloqa mavjud"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Sputnik SOS"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Favqulodda chaqiruvlar yoki SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Ish profili"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diqqat!"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner yordamida siz Android foydalanuvchi interfeysini tuzatish va o‘zingizga moslashtirishingiz mumkin. Ushbu tajribaviy funksiyalar o‘zgarishi, buzilishi yoki keyingi versiyalarda olib tashlanishi mumkin. Ehtiyot bo‘lib davom eting."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Orqaga qaytish ishorasi"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Asosiy ekran ishorasi"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Amal tugmasi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index b6c6967..6ad5db4 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> và các ứng dụng đang mở khác đã phát hiện thấy ảnh chụp màn hình này."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Thêm vào ghi chú"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Thêm đường liên kết"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Trình ghi màn hình"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đang xử lý video ghi màn hình"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Thông báo đang diễn ra về phiên ghi màn hình"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Trình bảo vệ m.hình"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Không làm phiền"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Chế độ ưu tiên"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Không có thiết bị nào được ghép nối"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Nhấn để kết nối/ngắt kết nối với một thiết bị"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bật Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Đã kết nối"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Chia sẻ âm thanh"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Nhấn để chuyển hoặc chia sẻ âm thanh"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ngắt kết nối"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"kích hoạt"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Mở phần Cài đặt"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Thiết bị khác"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Bật/tắt chế độ xem Tổng quan"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Chế độ ưu tiên"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Xong"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Cài đặt"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Đang bật"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Dịch vụ cung cấp chức năng này có quyền truy cập vào tất cả thông tin xuất hiện trên màn hình của bạn hoặc phát trên thiết bị của bạn trong khi ghi hoặc truyền, bao gồm cả thông tin như mật khẩu, thông tin thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Chia sẻ hoặc ghi một ứng dụng"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Chia sẻ màn hình của bạn với <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Chia sẻ một ứng dụng"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Chia sẻ toàn bộ màn hình"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Chia sẻ một ứng dụng"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Chia sẻ toàn bộ màn hình"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Khi bạn chia sẻ toàn bộ màn hình, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ thấy được mọi nội dung trên màn hình của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Khi bạn chia sẻ một ứng dụng, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ thấy được mọi nội dung hiển thị hoặc phát trong ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Chia sẻ màn hình"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Kết nối vệ tinh tốt"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Hiện có kết nối vệ tinh"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Liên lạc khẩn cấp qua vệ tinh"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Cuộc gọi khẩn cấp hoặc SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Hồ sơ công việc"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Thú vị đối với một số người nhưng không phải tất cả"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Bộ điều hướng giao diện người dùng hệ thống cung cấp thêm cho bạn những cách chỉnh sửa và tùy chỉnh giao diện người dùng Android. Những tính năng thử nghiệm này có thể thay đổi, hỏng hoặc biến mất trong các phiên bản tương lai. Hãy thận trọng khi tiếp tục."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Cử chỉ quay lại"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Cử chỉ chuyển đến màn hình chính"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Phím hành động"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9ce05a3..35f8851 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -104,10 +104,11 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> 及其他打开的应用检测到此屏幕截图。"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"添加到备注中"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"包括链接"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"屏幕录制器"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在处理屏幕录制视频"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持续显示屏幕录制会话通知"</string>
- <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要录制屏幕?"</string>
+ <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要录制屏幕吗?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"录制单个应用"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"录制整个屏幕"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"录制整个屏幕时,屏幕上显示的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"屏保"</string>
<string name="ethernet_label" msgid="2203544727007463351">"有线网络"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"勿扰"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"优先模式"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"蓝牙"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"没有可用的配对设备"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"点按即可连接设备或断开设备连接"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"启用蓝牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已连接"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音频分享"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"点按即可切换或分享音频"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已保存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"断开连接"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"启用"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"打开“设置”"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他设备"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切换概览"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"优先模式"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"设置"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"已开启"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"在录制或投放内容时,提供此功能的服务将可访问屏幕上显示或设备中播放的所有信息,其中包括密码、付款信息、照片、消息及播放的音频等信息。"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"分享或录制应用"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"要与“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”共享屏幕吗?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"共享一个应用"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"共享整个屏幕"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"共享一个应用"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"共享整个屏幕"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"当您共享整个屏幕时,屏幕上的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"当您共享一个应用时,该应用中显示或播放的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"共享屏幕"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"卫星,连接质量良好"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"卫星,可连接"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"卫星紧急呼救"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"紧急呼叫或紧急求救"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"工作资料"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"并不适合所有用户"</string>
<string name="tuner_warning" msgid="1861736288458481650">"系统界面调节工具可让您以更多方式调整及定制 Android 界面。在日后推出的版本中,这些实验性功能可能会变更、失效或消失。操作时请务必谨慎。"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返回手势"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主屏幕手势"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"快捷操作按键"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index e8532be..359b3cc 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> 和其他開啟的應用程式偵測到此螢幕截圖。"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"新增至筆記"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"加入連結"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"螢幕錄影機"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在處理螢幕錄影內容"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示錄影畫面工作階段通知"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"螢幕保護程式"</string>
<string name="ethernet_label" msgid="2203544727007463351">"以太網"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"請勿騷擾"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先模式"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"藍牙"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"找不到配對的裝置"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"輕按即可連結或解除連結裝置"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連接"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音訊分享功能"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"輕按即可切換或分享音訊"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"解除連結"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟動"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"開啟「設定」"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換概覽"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先模式"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"開啟"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"在錄影或投放時,此功能的服務供應商可存取螢幕顯示或裝置播放的任何資料,當中包括密碼、付款資料、相片、訊息和播放的語音等資料。"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"分享或錄影應用程式"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"要與「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」分享螢幕畫面嗎?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"分享一個應用程式"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"分享整個螢幕畫面"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"分享一個應用程式"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"分享整個螢幕畫面"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"當你分享整個螢幕畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可看到你畫面上的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"當你分享應用程式時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可看到該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"分享螢幕畫面"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛星,連線質素好"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛星,可以連線"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"緊急衛星連接"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"緊急電話或 SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"工作設定檔"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"這只是測試版本,並不包含完整功能"</string>
<string name="tuner_warning" msgid="1861736288458481650">"使用者介面調諧器讓你以更多方法修改和自訂 Android 使用者介面。但請小心,這些實驗功能可能會在日後發佈時更改、分拆或消失。"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返去手勢"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主畫面手勢"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"快捷操作鍵"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index e049374..63b19d6b 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"「<xliff:g id="APPNAME">%1$s</xliff:g>」和其他開啟的應用程式偵測到這張螢幕截圖。"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"新增至記事本"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"包含連結"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"螢幕錄影器"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"處理螢幕錄影內容"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示螢幕畫面錄製工作階段通知"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"螢幕保護程式"</string>
<string name="ethernet_label" msgid="2203544727007463351">"乙太網路"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"零打擾"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先模式"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"藍牙"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"找不到配對的裝置"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"輕觸即可連結/取消連結裝置"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連線"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音訊分享"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"輕觸即可切換或分享音訊"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"取消連結"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟用"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"開啟「設定」"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換總覽"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先模式"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"開啟"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"當你錄製或投放內容時,提供這項功能的服務將可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款資料、相片、訊息和你播放的音訊等資訊。"</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"分享或錄製應用程式"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"要使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」分享畫面嗎?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"分享單一應用程式的畫面"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"分享整個畫面"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"分享單一應用程式的畫面"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"分享整個畫面"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"當你分享整個畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取畫面上的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"當你分享應用程式畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取該應用程式顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"分享畫面"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛星,連線品質良好"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛星,可連線"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"緊急衛星連線"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"緊急電話或緊急求救"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"工作資料夾"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"有趣與否,見仁見智"</string>
<string name="tuner_warning" msgid="1861736288458481650">"系統使用者介面調整精靈可讓你透過其他方式,調整及自訂 Android 使用者介面。這些實驗性功能隨著版本更新可能會變更、損壞或消失,執行時請務必謹慎。"</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返回手勢"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主畫面手勢"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"快捷操作鍵"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index d85db55..048cb01 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -104,6 +104,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"I-<xliff:g id="APPNAME">%1$s</xliff:g> namanye ama-app avuliwe athole lesi sithombe-skrini."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Engeza kunothi"</string>
<string name="backlinks_include_link" msgid="4562093591148248158">"Faka ilinki"</string>
+ <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Okokuqopha iskrini"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Icubungula okokuqopha iskrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Isaziso esiqhubekayo seseshini yokurekhoda isikrini"</string>
@@ -291,7 +292,8 @@
<string name="start_dreams" msgid="9131802557946276718">"Isigciniskrini"</string>
<string name="ethernet_label" msgid="2203544727007463351">"I-Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ungaphazamisi"</string>
- <string name="quick_settings_modes_label" msgid="5407025818652750501">"Amamodi okubalulekile"</string>
+ <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"I-Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Awekho amadivayisi abhanqiwe atholakalayo"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Thepha ukuze uxhumae noma ungaxhumi idivaysi"</string>
@@ -300,6 +302,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Sebenzisa iBluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ixhunyiwe"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ukwabelana Ngokuqoshiwe"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Thepha ukuze ushintshe noma wabelane ngokulalelwayo"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"nqamula"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string>
@@ -431,7 +434,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Vula Amasethingi"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Enye idivayisi"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Guqula ukubuka konke"</string>
- <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Amamodi okubalulekile"</string>
+ <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+ <skip />
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kwenziwe"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Amasethingi"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Vuliwe"</string>
@@ -532,8 +536,12 @@
<string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Isevisi enikezela ngalo msebenzi izothola ukufinyelela kulo lonke ulwazi olubonakalayo esikrinini sakho noma oludlalwa kusuka kudivayisi yakho ngenkathi urekhoda noma usakaza. Lokhu kubandakanya ulwazi olufana namaphasiwedi, imininingwane yenkokhelo, izithombe, imilayezo, nomsindo owudlalayo."</string>
<string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Yabelana noma urekhode i-app"</string>
<string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Yabelana ngesikrini sakho ne-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app" msgid="6314402084788062644">"Yabelana nge-app eyodwa"</string>
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen" msgid="7381488112332599632">"Yabelana ngesikrini sonke"</string>
+ <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Yabelana nge-app eyodwa"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+ <skip />
+ <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Yabelana ngesikrini sonke"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+ <skip />
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Uma wabelana ngesikrini sakho sonke, noma yini esesikrinini sakho ibonakala ku-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Uma wabelana nge-app, noma yini eboniswayo noma edlalwayo kuleyo app ibonakala ku-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Yabelana ngesikrini"</string>
@@ -718,8 +726,7 @@
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Isethelayithi, uxhumano oluhle"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Isethelayithi, uxhumano luyatholakala"</string>
<string name="satellite_connected_carrier_text" msgid="118524195198532589">"Isethelayithi yokuxhumana ngezimo eziphuthumayo"</string>
- <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) -->
- <skip />
+ <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Ikholi ephuthumayo noma i-SOS"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Iphrofayela yomsebenzi"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Kuyajabulisa kwabanye kodwa hhayi bonke"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Isishuni se-UI sesistimu sikunika izindlela ezingeziwe zokuhlobisa nokwenza ngezifiso isixhumanisi sokubona se-Android. Lezi zici zesilingo zingashintsha, zephuke, noma zinyamalale ekukhishweni kwangakusasa. Qhubeka ngokuqaphela."</string>
@@ -1386,6 +1393,18 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string>
+ <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+ <skip />
+ <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+ <skip />
+ <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+ <skip />
<string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Ukunyakazisa umzimba kwangemuva"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Ukunyakazisa umzimba kwasekhaya"</string>
<string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Inkinobho yokufinyelela"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8cf0fb2..a375264 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -29,7 +29,7 @@
<color name="status_bar_icons_hover_color_dark">#38000000</color> <!-- 22% black -->
<!-- The dark background color behind the shade -->
- <color name="shade_scrim_background_dark">@*android:color/black</color>
+ <color name="shade_scrim_background_dark">@androidprv:color/system_under_surface_light</color>
<!-- The color of the background in the separated list of the Global Actions menu -->
<color name="global_actions_separated_background">#F5F5F5</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e8fd2ef..38ef0e9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1052,9 +1052,6 @@
<!-- The width of the shortcut helper container, as a fraction of the screen's width. -->
<item name="shortcut_helper_screen_width_fraction" format="float" type="dimen">1.0</item>
- <!-- Only applicable for dual shade - Allow Notifications/QS shade to anchor to the bottom. -->
- <bool name="config_dualShadeAlignedToBottom">false</bool>
-
<!-- List of packages for which we want to use activity info (instead of application info) for biometric prompt logo. Empty for AOSP. [DO NOT TRANSLATE] -->
<string-array name="config_useActivityLogoForBiometricPrompt" translatable="false"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e5750d2..e1808fa 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1751,6 +1751,7 @@
<!-- System UI Dialog -->
<dimen name="dialog_title_text_size">24sp</dimen>
+ <dimen name="dialog_button_side_margin">8dp</dimen>
<!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_max_height">662dp</dimen>
@@ -2001,10 +2002,10 @@
<!-- Shadow for dream overlay status bar complications -->
<dimen name="dream_overlay_status_bar_key_text_shadow_dx">0.5dp</dimen>
<dimen name="dream_overlay_status_bar_key_text_shadow_dy">0.5dp</dimen>
- <dimen name="dream_overlay_status_bar_key_text_shadow_radius">1dp</dimen>
+ <dimen name="dream_overlay_status_bar_key_text_shadow_radius">3dp</dimen>
<dimen name="dream_overlay_status_bar_ambient_text_shadow_dx">0.5dp</dimen>
<dimen name="dream_overlay_status_bar_ambient_text_shadow_dy">0.5dp</dimen>
- <dimen name="dream_overlay_status_bar_ambient_text_shadow_radius">2dp</dimen>
+ <dimen name="dream_overlay_status_bar_ambient_text_shadow_radius">3dp</dimen>
<dimen name="dream_overlay_icon_inset_dimen">0dp</dimen>
<!-- Default device corner radius, used for assist UI -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e6cc6cf..589d9dd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -269,7 +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>
@@ -720,8 +725,8 @@
<!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Do not disturb - Alarms only [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Do not disturb - Total silence [CHAR LIMIT=NONE] -->
- <!-- QuickSettings: Priority modes [CHAR LIMIT=NONE] -->
- <string name="quick_settings_modes_label">Priority modes</string>
+ <!-- QuickSettings: Modes [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_modes_label">Modes</string>
<!-- QuickSettings: Bluetooth [CHAR LIMIT=NONE] -->
<string name="quick_settings_bluetooth_label">Bluetooth</string>
<!-- QuickSettings: Bluetooth (Multiple) [CHAR LIMIT=NONE] -->
@@ -1096,28 +1101,28 @@
<!-- QuickStep: Accessibility to toggle overview [CHAR LIMIT=40] -->
<string name="quick_step_accessibility_toggle_overview">Toggle Overview</string>
- <!-- Priority modes dialog title [CHAR LIMIT=35] -->
- <string name="zen_modes_dialog_title">Priority modes</string>
+ <!-- Modes dialog title [CHAR LIMIT=35] -->
+ <string name="zen_modes_dialog_title">Modes</string>
- <!-- Priority modes dialog confirmation button [CHAR LIMIT=15] -->
+ <!-- Modes dialog confirmation button [CHAR LIMIT=15] -->
<string name="zen_modes_dialog_done">Done</string>
- <!-- Priority modes dialog settings shortcut button [CHAR LIMIT=15] -->
+ <!-- Modes dialog settings shortcut button [CHAR LIMIT=15] -->
<string name="zen_modes_dialog_settings">Settings</string>
- <!-- Priority modes: label for an active mode [CHAR LIMIT=35] -->
+ <!-- Modes: label for an active mode [CHAR LIMIT=35] -->
<string name="zen_mode_on">On</string>
- <!-- Priority modes: label for an active mode, with details [CHAR LIMIT=10] -->
+ <!-- Modes: label for an active mode, with details [CHAR LIMIT=10] -->
<string name="zen_mode_on_with_details">On • <xliff:g id="trigger_description" example="Mon-Fri, 23:00-7:00">%1$s</xliff:g></string>
- <!-- Priority modes: label for an inactive mode [CHAR LIMIT=35] -->
+ <!-- Modes: label for an inactive mode [CHAR LIMIT=35] -->
<string name="zen_mode_off">Off</string>
- <!-- Priority modes: label for a mode that needs to be set up [CHAR LIMIT=35] -->
+ <!-- Modes: label for a mode that needs to be set up [CHAR LIMIT=35] -->
<string name="zen_mode_set_up">Set up</string>
- <!-- Priority modes: label for a mode that cannot be manually turned on [CHAR LIMIT=35] -->
+ <!-- Modes: label for a mode that cannot be manually turned on [CHAR LIMIT=35] -->
<string name="zen_mode_no_manual_invocation">Manage in settings</string>
<string name="zen_mode_active_modes">
@@ -1379,9 +1384,13 @@
<string name="media_projection_entry_app_permission_dialog_title">Share your screen with <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g>?</string>
<!-- 1P/3P app media projection permission option for capturing just a single app [CHAR LIMIT=50] -->
- <string name="media_projection_entry_app_permission_dialog_option_text_single_app">Share one app</string>
+ <string name="screen_share_permission_dialog_option_single_app">Share one app</string>
+ <!-- CTS tests rely on the `screen_share_permission_dialog_option_single_app` resource name, so just point the updated resource name to the old resource name. -->
+ <string name="media_projection_entry_app_permission_dialog_option_text_single_app">@string/screen_share_permission_dialog_option_single_app</string>
<!-- 1P/3P app media projection permission option for capturing the whole screen [CHAR LIMIT=50] -->
- <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen">Share entire screen</string>
+ <string name="screen_share_permission_dialog_option_entire_screen">Share entire screen</string>
+ <!-- CTS tests rely on the `screen_share_permission_dialog_option_entire_screen` resource name, so just point the updated resource name to the old resource name. -->
+ <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen">@string/screen_share_permission_dialog_option_entire_screen</string>
<!-- 1P/3P app media projection permission warning for capturing the whole screen. [CHAR LIMIT=350] -->
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen">When you’re sharing your entire screen, anything on your screen is visible to <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
<!-- 1P/3P app media projection permission warning for capturing an app. [CHAR LIMIT=350] -->
@@ -3694,6 +3703,22 @@
-->
<string name="shortcut_helper_key_combinations_or_separator">or</string>
+ <!-- Keyboard touchpad tutorial scheduler-->
+ <!-- Notification title for launching keyboard tutorial [CHAR_LIMIT=100] -->
+ <string name="launch_keyboard_tutorial_notification_title">Navigate using your keyboard</string>
+ <!-- Notification text for launching keyboard tutorial [CHAR_LIMIT=100] -->
+ <string name="launch_keyboard_tutorial_notification_content">Learn keyboards shortcuts</string>
+
+ <!-- Notification title for launching touchpad tutorial [CHAR_LIMIT=100] -->
+ <string name="launch_touchpad_tutorial_notification_title">Navigate using your touchpad</string>
+ <!-- Notification text for launching keyboard tutorial [CHAR_LIMIT=100] -->
+ <string name="launch_touchpad_tutorial_notification_content">Learn touchpad gestures</string>
+
+ <!-- Notification title for launching keyboard tutorial [CHAR_LIMIT=100] -->
+ <string name="launch_keyboard_touchpad_tutorial_notification_title">Navigate using your keyboard and touchpad</string>
+ <!-- Notification text for launching keyboard tutorial [CHAR_LIMIT=100] -->
+ <string name="launch_keyboard_touchpad_tutorial_notification_content">Learn touchpad gestures, keyboards shortcuts, and more</string>
+
<!-- TOUCHPAD TUTORIAL-->
<!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] -->
<string name="touchpad_tutorial_back_gesture_button">Back gesture</string>
@@ -3787,4 +3812,33 @@
<!-- Toast message for notifying users to use regular brightness bar to lower the brightness. [CHAR LIMIT=NONE] -->
<string name="accessibility_deprecate_extra_dim_dialog_toast">
Extra dim shortcut removed. To lower your brightness, use the regular brightness bar.</string>
+
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to connectivity, e.g. Internet. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_connectivity">
+ Connectivity
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to accessibility functions, e.g. Hearing devices. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_accessibility">
+ Accessibility
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to general utilities, e.g. Flashlight. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_utilities">
+ Utilities
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to privacy, e.g. Mic access. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_privacy">
+ Privacy
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are provided by an app, e.g. Calculator. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_providedByApps">
+ Provided by apps
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to the display, e.g. Dark theme. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_display">
+ Display
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles with an unknown category. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_unknown">
+ Unknown
+ </string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3ef6243..a02c354 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -505,14 +505,14 @@
<item name="onSurfaceVariant">?androidprv:attr/materialColorOnSurfaceVariant</item>
<item name="outline">?androidprv:attr/materialColorOutline</item>
- <item name="shadeActive">@color/material_dynamic_primary90</item>
- <item name="onShadeActive">@color/material_dynamic_primary10</item>
- <item name="onShadeActiveVariant">@color/material_dynamic_primary30</item>
- <item name="shadeInactive">@color/material_dynamic_neutral20</item>
- <item name="onShadeInactive">@color/material_dynamic_neutral90</item>
- <item name="onShadeInactiveVariant">@color/material_dynamic_neutral_variant80</item>
- <item name="shadeDisabled">@color/shade_disabled</item>
- <item name="underSurface">@color/material_dynamic_neutral0</item>
+ <item name="shadeActive">?androidprv:attr/customColorShadeActive</item>
+ <item name="onShadeActive">?androidprv:attr/customColorOnShadeActive</item>
+ <item name="onShadeActiveVariant">?androidprv:attr/customColorOnShadeActiveVariant</item>
+ <item name="shadeInactive">?androidprv:attr/customColorShadeInactive</item>
+ <item name="onShadeInactive">?androidprv:attr/customColorOnShadeInactive</item>
+ <item name="onShadeInactiveVariant">?androidprv:attr/customColorOnShadeInactiveVariant</item>
+ <item name="shadeDisabled">?androidprv:attr/customColorShadeDisabled</item>
+ <item name="underSurface">?androidprv:attr/customColorUnderSurface</item>
<item name="android:itemTextAppearance">@style/Control.MenuItem</item>
</style>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index e68da09..8f55961 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -49,7 +49,6 @@
"src/**/*.aidl",
":wm_shell-aidls",
":wm_shell-shared-aidls",
- ":wm_shell_util-sources",
],
static_libs: [
"BiometricsSharedLib",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
index 57a49c8..3e39ae9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
@@ -15,10 +15,7 @@
*/
package com.android.systemui.dagger.qualifiers
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy.RUNTIME
import javax.inject.Qualifier
/** Annotates a class that is display specific. */
-@Qualifier @Documented @Retention(RUNTIME) annotation class DisplaySpecific
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class DisplaySpecific
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index c00007b..283e455 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -132,4 +132,9 @@
* Sent when {@link TaskbarDelegate#transitionTo} is called.
*/
void transitionTo(int barMode, boolean animate) = 33;
+
+ /**
+ * Sent when {@link TaskbarDelegate#appTransitionPending} is called.
+ */
+ void appTransitionPending(boolean pending) = 34;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
index b8319e5..c8de9f6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
@@ -36,36 +36,6 @@
public class RecentsTransition {
/**
- * Creates a new transition aspect scaled transition activity options.
- */
- public static ActivityOptions createAspectScaleAnimation(Context context, Handler handler,
- boolean scaleUp, AppTransitionAnimationSpecsFuture animationSpecsFuture,
- final Runnable animationStartCallback) {
- final OnAnimationStartedListener animStartedListener = new OnAnimationStartedListener() {
- private boolean mHandled;
-
- @Override
- public void onAnimationStarted(long elapsedRealTime) {
- // OnAnimationStartedListener can be called numerous times, so debounce here to
- // prevent multiple callbacks
- if (mHandled) {
- return;
- }
- mHandled = true;
-
- if (animationStartCallback != null) {
- animationStartCallback.run();
- }
- }
- };
- final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(
- context, handler,
- animationSpecsFuture != null ? animationSpecsFuture.getFuture() : null,
- animStartedListener, scaleUp);
- return opts;
- }
-
- /**
* Wraps a animation-start callback in a binder that can be called from window manager.
*/
public static IRemoteCallback wrapStartedListener(final Handler handler,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index bbf4698..76af813 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -18,13 +18,13 @@
import android.os.RemoteException;
import android.util.Log;
-import android.view.IRecentsAnimationController;
import android.view.SurfaceControl;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
import com.android.internal.os.IResultReceiver;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.wm.shell.recents.IRecentsAnimationController;
public class RecentsAnimationControllerCompat {
@@ -58,14 +58,6 @@
}
}
- public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) {
- try {
- mAnimationController.setAnimationTargetsBehindSystemBars(behindSystemBars);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to set whether animation targets are behind system bars", e);
- }
- }
-
/**
* Sets the final surface transaction on a Task. This is used by Launcher to notify the system
* that animating Activity to PiP has completed and the associated task surface should be
@@ -103,22 +95,6 @@
}
}
- public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
- try {
- mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to set deferred cancel with screenshot", e);
- }
- }
-
- public void cleanupScreenshot() {
- try {
- mAnimationController.cleanupScreenshot();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to clean up screenshot of recents animation", e);
- }
- }
-
/**
* @see {{@link IRecentsAnimationController#setWillFinishToHome(boolean)}}.
*/
@@ -131,18 +107,6 @@
}
/**
- * @see IRecentsAnimationController#removeTask
- */
- public boolean removeTask(int taskId) {
- try {
- return mAnimationController.removeTask(taskId);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to remove remote animation target", e);
- return false;
- }
- }
-
- /**
* @see IRecentsAnimationController#detachNavigationBarFromApp
*/
public void detachNavigationBarFromApp(boolean moveHomeToTop) {
@@ -152,15 +116,4 @@
Log.e(TAG, "Failed to detach the navigation bar from app", e);
}
}
-
- /**
- * @see IRecentsAnimationController#animateNavigationBarToApp(long)
- */
- public void animateNavigationBarToApp(long duration) {
- try {
- mAnimationController.animateNavigationBarToApp(duration);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to animate the navigation bar to app", e);
- }
- }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index 2407350..51892aa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -42,14 +42,4 @@
* was running becomes ready for control.
*/
void onTasksAppeared(RemoteAnimationTarget[] app);
-
- /**
- * Called to request that the current task tile be switched out for a screenshot (if not
- * already). Once complete, onFinished should be called.
- * @return true if this impl will call onFinished. No other onSwitchToScreenshot impls will
- * be called afterwards (to avoid multiple calls to onFinished).
- */
- default boolean onSwitchToScreenshot(Runnable onFinished) {
- return false;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt b/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt
new file mode 100644
index 0000000..efa13c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.keyguard
+
+import android.os.VibrationAttributes
+import com.google.android.msdl.domain.InteractionProperties
+
+/**
+ * This class represents the set of [InteractionProperties] that only hold [VibrationAttributes] for
+ * the case of user authentication.
+ */
+data class AuthInteractionProperties(
+ override val vibrationAttributes: VibrationAttributes =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST)
+) : InteractionProperties
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index 7733841..5e36539 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.Flags.msdlFeedback;
import android.annotation.SuppressLint;
import android.app.ActivityOptions;
@@ -46,6 +47,9 @@
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.ViewController;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -67,6 +71,7 @@
private final Executor mMainExecutor;
private final Executor mBackgroundExecutor;
private final SelectedUserInteractor mSelectedUserInteractor;
+ private final MSDLPlayer mMSDLPlayer;
private final KeyguardUpdateMonitorCallback mInfoCallback =
new KeyguardUpdateMonitorCallback() {
@@ -99,7 +104,8 @@
MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
Executor mainExecutor, Executor backgroundExecutor,
- SelectedUserInteractor selectedUserInteractor) {
+ SelectedUserInteractor selectedUserInteractor,
+ MSDLPlayer msdlPlayer) {
super(view);
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -112,6 +118,7 @@
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
mSelectedUserInteractor = selectedUserInteractor;
+ mMSDLPlayer = msdlPlayer;
}
@Override
@@ -165,6 +172,9 @@
@SuppressLint("MissingPermission")
public void takeEmergencyCallAction() {
mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL);
+ if (msdlFeedback()) {
+ mMSDLPlayer.playToken(MSDLToken.KEYPRESS_RETURN, null);
+ }
if (mPowerManager != null) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
}
@@ -221,6 +231,7 @@
private final Executor mMainExecutor;
private final Executor mBackgroundExecutor;
private final SelectedUserInteractor mSelectedUserInteractor;
+ private final MSDLPlayer mMSDLPlayer;
@Inject
public Factory(ConfigurationController configurationController,
@@ -233,7 +244,8 @@
LockPatternUtils lockPatternUtils,
@Main Executor mainExecutor,
@Background Executor backgroundExecutor,
- SelectedUserInteractor selectedUserInteractor) {
+ SelectedUserInteractor selectedUserInteractor,
+ MSDLPlayer msdlPlayer) {
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -246,6 +258,7 @@
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
mSelectedUserInteractor = selectedUserInteractor;
+ mMSDLPlayer = msdlPlayer;
}
/** Construct an {@link com.android.keyguard.EmergencyButtonController}. */
@@ -253,7 +266,7 @@
return new EmergencyButtonController(view, mConfigurationController,
mKeyguardUpdateMonitor, mPowerManager, mActivityTaskManager, mShadeController,
mTelecomManager, mMetricsLogger, mLockPatternUtils, mMainExecutor,
- mBackgroundExecutor, mSelectedUserInteractor);
+ mBackgroundExecutor, mSelectedUserInteractor, mMSDLPlayer);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index dad4400..28f1381 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -19,7 +19,10 @@
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
+import static com.android.systemui.Flags.msdlFeedback;
+import static com.android.systemui.Flags.notifyPasswordTextViewUserActivityInBackground;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.CountDownTimer;
@@ -40,6 +43,9 @@
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import java.util.HashMap;
import java.util.Map;
@@ -50,11 +56,14 @@
private final LatencyTracker mLatencyTracker;
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
+ private final UserActivityNotifier mUserActivityNotifier;
private CountDownTimer mCountdownTimer;
private boolean mDismissing;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected boolean mResumed;
protected boolean mLockedOut;
+ @Nullable
+ protected MSDLPlayer mMSDLPlayer;
private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
// Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
@@ -81,7 +90,9 @@
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController,
- FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
+ FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
messageAreaControllerFactory, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -89,6 +100,8 @@
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
+ mMSDLPlayer = msdlPlayer;
+ mUserActivityNotifier = userActivityNotifier;
}
abstract void resetState();
@@ -178,6 +191,7 @@
void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
if (matched) {
+ playAuthenticationHaptics(/* unlock= */true);
getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
mDismissing = true;
@@ -185,6 +199,7 @@
getKeyguardSecurityCallback().dismiss(true, userId, getSecurityMode());
}
} else {
+ playAuthenticationHaptics(/* unlock= */false);
mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
if (isValidPassword) {
getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
@@ -201,6 +216,18 @@
}
}
+ private void playAuthenticationHaptics(boolean unlock) {
+ if (!msdlFeedback() || mMSDLPlayer == null) return;
+
+ MSDLToken token;
+ if (unlock) {
+ token = MSDLToken.UNLOCK;
+ } else {
+ token = MSDLToken.FAILURE;
+ }
+ mMSDLPlayer.playToken(token, mAuthInteractionProperties);
+ }
+
protected void startErrorAnimation() { /* no-op */ }
protected void verifyPasswordAndUnlock() {
@@ -280,6 +307,9 @@
getKeyguardSecurityCallback().userActivity();
getKeyguardSecurityCallback().onUserInput();
mMessageAreaController.setMessage("");
+ if (notifyPasswordTextViewUserActivityInBackground()) {
+ mUserActivityNotifier.notifyUserActivity();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index db14a0f..92e5432 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -45,6 +45,9 @@
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.google.android.msdl.domain.InteractionProperties;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import javax.inject.Inject;
/** Controller for a {@link KeyguardSecurityView}. */
@@ -63,6 +66,8 @@
private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
private final FeatureFlags mFeatureFlags;
protected final SelectedUserInteractor mSelectedUserInteractor;
+ protected final InteractionProperties mAuthInteractionProperties =
+ new AuthInteractionProperties();
protected KeyguardInputViewController(T view, SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
@@ -214,6 +219,8 @@
private final SelectedUserInteractor mSelectedUserInteractor;
private final UiEventLogger mUiEventLogger;
private final KeyguardKeyboardInteractor mKeyguardKeyboardInteractor;
+ private final MSDLPlayer mMSDLPlayer;
+ private final UserActivityNotifier mUserActivityNotifier;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -228,7 +235,9 @@
KeyguardViewController keyguardViewController,
FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
UiEventLogger uiEventLogger,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -246,6 +255,8 @@
mSelectedUserInteractor = selectedUserInteractor;
mUiEventLogger = uiEventLogger;
mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
+ mMSDLPlayer = msdlPlayer;
+ mUserActivityNotifier = userActivityNotifier;
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -260,7 +271,8 @@
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
emergencyButtonController, mMessageAreaControllerFactory,
- mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
+ mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
+ mMSDLPlayer);
} else if (keyguardInputView instanceof KeyguardPasswordView) {
return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
@@ -268,29 +280,29 @@
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
mFalsingCollector, mKeyguardViewController,
mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor);
+ mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier);
} else if (keyguardInputView instanceof KeyguardPINView) {
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
- mUiEventLogger, mKeyguardKeyboardInteractor
- );
+ mUiEventLogger, mKeyguardKeyboardInteractor, mMSDLPlayer,
+ mUserActivityNotifier);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor);
+ mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor
+ mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier
);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 3ad73bc..905fa09 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -19,6 +19,7 @@
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -55,6 +56,8 @@
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import java.util.List;
public class KeyguardPasswordViewController
@@ -134,10 +137,13 @@
DevicePostureController postureController,
FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags, selectedUserInteractor);
+ emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer,
+ userActivityNotifier);
mKeyguardSecurityCallback = keyguardSecurityCallback;
mInputMethodManager = inputMethodManager;
mPostureController = postureController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index caa74780..f74d93e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -36,6 +36,7 @@
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.bouncer.ui.helper.BouncerHapticHelper;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
@@ -43,6 +44,8 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -67,6 +70,7 @@
private LockPatternView mLockPatternView;
private CountDownTimer mCountdownTimer;
private AsyncTask<?, ?, ?> mPendingLockCheck;
+ private MSDLPlayer mMSDLPlayer;
private EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
@Override
@@ -75,6 +79,10 @@
}
};
+ private final LockPatternView.ExternalHapticsPlayer mExternalHapticsPlayer = () -> {
+ BouncerHapticHelper.INSTANCE.playPatternDotFeedback(mMSDLPlayer, mView);
+ };
+
/**
* Useful for clearing out the wrong pattern after a delay
*/
@@ -166,6 +174,10 @@
boolean isValidPattern) {
boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
if (matched) {
+ BouncerHapticHelper.INSTANCE.playMSDLAuthenticationFeedback(
+ /* authenticationSucceeded= */true,
+ /* player =*/mMSDLPlayer
+ );
getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
@@ -173,6 +185,10 @@
getKeyguardSecurityCallback().dismiss(true, userId, SecurityMode.Pattern);
}
} else {
+ BouncerHapticHelper.INSTANCE.playMSDLAuthenticationFeedback(
+ /* authenticationSucceeded= */false,
+ /* player =*/mMSDLPlayer
+ );
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
if (isValidPattern) {
getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
@@ -200,7 +216,7 @@
EmergencyButtonController emergencyButtonController,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
DevicePostureController postureController, FeatureFlags featureFlags,
- SelectedUserInteractor selectedUserInteractor) {
+ SelectedUserInteractor selectedUserInteractor, MSDLPlayer msdlPlayer) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
messageAreaControllerFactory, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -212,6 +228,7 @@
featureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
mLockPatternView = mView.findViewById(R.id.lockPatternView);
mPostureController = postureController;
+ mMSDLPlayer = msdlPlayer;
}
@Override
@@ -249,6 +266,7 @@
if (deadline != 0) {
handleAttemptLockout(deadline);
}
+ mLockPatternView.setExternalHapticsPlayer(mExternalHapticsPlayer);
}
@Override
@@ -262,6 +280,7 @@
cancelBtn.setOnClickListener(null);
}
mPostureController.removeCallback(mPostureCallback);
+ mLockPatternView.setExternalHapticsPlayer(null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 0f61233..f575cf2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -16,9 +16,11 @@
package com.android.keyguard;
+import static com.android.systemui.Flags.msdlFeedback;
import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.annotation.Nullable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
@@ -40,6 +42,9 @@
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
extends KeyguardAbsKeyInputViewController<T> {
@@ -77,10 +82,13 @@
FalsingCollector falsingCollector,
FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags, selectedUserInteractor);
+ emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer,
+ userActivityNotifier);
mLiftToActivateListener = liftToActivateListener;
mFalsingCollector = falsingCollector;
mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
@@ -102,12 +110,22 @@
return false;
});
button.setAnimationEnabled(showAnimations);
+ button.setMSDLPlayer(mMSDLPlayer);
}
mPasswordEntry.setOnKeyListener(mOnKeyListener);
mPasswordEntry.setUserActivityListener(this::onUserInput);
View deleteButton = mView.findViewById(R.id.delete_button);
- deleteButton.setOnTouchListener(mActionButtonTouchListener);
+ if (msdlFeedback()) {
+ deleteButton.setOnTouchListener((View view, MotionEvent event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mMSDLPlayer != null) {
+ mMSDLPlayer.playToken(MSDLToken.KEYPRESS_DELETE, null);
+ }
+ return false;
+ });
+ } else {
+ deleteButton.setOnTouchListener(mActionButtonTouchListener);
+ }
deleteButton.setOnClickListener(v -> {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
@@ -119,13 +137,19 @@
if (mPasswordEntry.isEnabled()) {
mView.resetPasswordText(true /* animate */, true /* announce */);
}
- mView.doHapticKeyClick();
+ if (msdlFeedback() && mMSDLPlayer != null) {
+ mMSDLPlayer.playToken(MSDLToken.LONG_PRESS, null);
+ } else {
+ mView.doHapticKeyClick();
+ }
return true;
});
View okButton = mView.findViewById(R.id.key_enter);
if (okButton != null) {
- okButton.setOnTouchListener(mActionButtonTouchListener);
+ if (!msdlFeedback()) {
+ okButton.setOnTouchListener(mActionButtonTouchListener);
+ }
okButton.setOnClickListener(v -> {
if (mPasswordEntry.isEnabled()) {
verifyPasswordAndUnlock();
@@ -177,6 +201,7 @@
for (NumPadKey button : mView.getButtons()) {
button.setOnTouchListener(null);
+ button.setMSDLPlayer(null);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index f4cda02..3b5bf1a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -18,6 +18,7 @@
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
+import android.annotation.Nullable;
import android.view.View;
import com.android.internal.logging.UiEvent;
@@ -33,6 +34,8 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
public class KeyguardPinViewController
extends KeyguardPinBasedInputViewController<KeyguardPINView> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -61,11 +64,13 @@
FalsingCollector falsingCollector,
DevicePostureController postureController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor, UiEventLogger uiEventLogger,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor);
+ keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPostureController = postureController;
mLockPatternUtils = lockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 61f9800..2d28a18 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -35,7 +35,6 @@
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
-import android.app.admin.flags.Flags;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -1140,12 +1139,7 @@
int remainingBeforeWipe, int failedAttempts) {
int userType = USER_TYPE_PRIMARY;
if (expiringUserId == userId) {
- int primaryUser = UserHandle.USER_SYSTEM;
- if (Flags.headlessSingleUserFixes()) {
- if (mainUserId != null) {
- primaryUser = mainUserId;
- }
- }
+ int primaryUser = mainUserId != null ? mainUserId : UserHandle.USER_SYSTEM;
// TODO: http://b/23522538
if (expiringUserId != primaryUser) {
userType = USER_TYPE_SECONDARY_USER;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 3ef3418..47fe2b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -21,6 +21,7 @@
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
@@ -48,6 +49,8 @@
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
public class KeyguardSimPinViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
public static final String TAG = "KeyguardSimPinView";
@@ -95,11 +98,13 @@
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor);
+ keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 46225c7..c688acb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -43,6 +44,8 @@
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
public class KeyguardSimPukViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -92,11 +95,13 @@
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor);
+ keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index dcfa775..4fb80de 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -15,6 +15,7 @@
*/
package com.android.keyguard;
+import static com.android.systemui.Flags.msdlFeedback;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY;
import android.content.Context;
@@ -38,6 +39,9 @@
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
/**
* Viewgroup for the bouncer numpad button, specifically for digits.
*/
@@ -57,6 +61,8 @@
@Nullable
private NumPadAnimator mAnimator;
private int mOrientation;
+ @Nullable
+ private MSDLPlayer mMSDLPlayer;
private View.OnClickListener mListener = new View.OnClickListener() {
@Override
@@ -221,8 +227,12 @@
// Cause a VIRTUAL_KEY vibration
public void doHapticKeyClick() {
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ if (msdlFeedback() && mMSDLPlayer != null) {
+ mMSDLPlayer.playToken(MSDLToken.KEYPRESS_STANDARD, null);
+ } else {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
}
@Override
@@ -244,4 +254,8 @@
super.onInitializeAccessibilityNodeInfo(info);
info.setTextEntryKey(true);
}
+
+ public void setMSDLPlayer(@Nullable MSDLPlayer player) {
+ mMSDLPlayer = player;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 85f8b48..0c4bc0e 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.android.systemui.Flags.notifyPasswordTextViewUserActivityInBackground;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -257,7 +259,9 @@
@Override
protected void onUserActivity() {
- mPM.userActivity(SystemClock.uptimeMillis(), false);
+ if (!notifyPasswordTextViewUserActivityInBackground()) {
+ mPM.userActivity(SystemClock.uptimeMillis(), false);
+ }
super.onUserActivity();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.kt b/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.kt
new file mode 100644
index 0000000..9b1ddb74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.keyguard
+
+import android.os.PowerManager
+import android.os.SystemClock
+import com.android.systemui.dagger.qualifiers.UiBackground
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/** Wrapper class for notifying the system about user activity in the background. */
+class UserActivityNotifier
+@Inject
+constructor(
+ @UiBackground private val uiBgExecutor: Executor,
+ private val powerManager: PowerManager
+) {
+
+ fun notifyUserActivity() {
+ uiBgExecutor.execute {
+ powerManager.userActivity(
+ SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0
+ )
+ }
+ }
+}
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/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
index 2c97d62..4d5e717 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
@@ -28,6 +28,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -68,8 +69,9 @@
}
@Inject
- public AccessibilityButtonModeObserver(Context context, UserTracker userTracker) {
- super(context, userTracker, Settings.Secure.ACCESSIBILITY_BUTTON_MODE);
+ public AccessibilityButtonModeObserver(
+ Context context, UserTracker userTracker, SecureSettings secureSettings) {
+ super(context, userTracker, secureSettings, Settings.Secure.ACCESSIBILITY_BUTTON_MODE);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java
index 53a21b3..1363b1c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java
@@ -24,6 +24,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -49,8 +50,9 @@
}
@Inject
- public AccessibilityButtonTargetsObserver(Context context, UserTracker userTracker) {
- super(context, userTracker, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+ public AccessibilityButtonTargetsObserver(
+ Context context, UserTracker userTracker, SecureSettings secureSettings) {
+ super(context, userTracker, secureSettings, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java
index c944878..736217a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java
@@ -24,6 +24,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -49,8 +50,9 @@
}
@Inject
- public AccessibilityGestureTargetsObserver(Context context, UserTracker userTracker) {
- super(context, userTracker, Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS);
+ public AccessibilityGestureTargetsObserver(
+ Context context, UserTracker userTracker, SecureSettings secureSettings) {
+ super(context, userTracker, secureSettings, Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 394f8dd..04afd86 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -408,6 +408,10 @@
if (!isActivated()) {
return;
}
+ if (!(mFullscreenBorder.getBackground() instanceof GradientDrawable)) {
+ // Wear doesn't use the same magnification border background. So early return here.
+ return;
+ }
float cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
GradientDrawable backgroundDrawable = (GradientDrawable) mFullscreenBorder.getBackground();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
index 443441f..eb4de68 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
@@ -18,6 +18,9 @@
import static android.view.WindowManager.LayoutParams;
+import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
+import static com.android.systemui.Flags.enableViewCaptureTracing;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -29,8 +32,8 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.WindowManager;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.res.R;
/**
@@ -70,11 +73,12 @@
* @see #setDefaultPosition(LayoutParams)
*/
private final Point mControlPosition = new Point();
- private final WindowManager mWindowManager;
+ private final ViewCaptureAwareWindowManager mWindowManager;
MirrorWindowControl(Context context) {
mContext = context;
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mWindowManager = getViewCaptureAwareWindowManagerInstance(mContext,
+ enableViewCaptureTracing());
}
public void setWindowDelegate(@Nullable MirrorWindowDelegate windowDelegate) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
index 326773f..c50cf85 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
@@ -48,6 +49,7 @@
private final UserTracker mUserTracker;
@VisibleForTesting
final ContentObserver mContentObserver;
+ private final SecureSettings mSecureSettings;
private final String mKey;
@@ -55,7 +57,7 @@
final List<T> mListeners = new ArrayList<>();
protected SecureSettingsContentObserver(Context context, UserTracker userTracker,
- String secureSettingsKey) {
+ SecureSettings secureSettings, String secureSettingsKey) {
mKey = secureSettingsKey;
mContentResolver = context.getContentResolver();
mUserTracker = userTracker;
@@ -65,6 +67,7 @@
updateValueChanged();
}
};
+ mSecureSettings = secureSettings;
}
/**
@@ -80,9 +83,8 @@
}
if (mListeners.size() == 1) {
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(mKey), /* notifyForDescendants= */
- false, mContentObserver, UserHandle.USER_ALL);
+ mSecureSettings.registerContentObserverForUserAsync(Settings.Secure.getUriFor(mKey),
+ /* notifyForDescendants= */ false, mContentObserver, UserHandle.USER_ALL);
}
}
@@ -97,7 +99,7 @@
mListeners.remove(listener);
if (mListeners.isEmpty()) {
- mContentResolver.unregisterContentObserver(mContentObserver);
+ mSecureSettings.unregisterContentObserverAsync(mContentObserver);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 7750f6b..51c5b00 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -18,8 +18,6 @@
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS;
-import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
-
import android.accessibilityservice.AccessibilityService;
import android.app.PendingIntent;
import android.app.RemoteAction;
@@ -45,7 +43,6 @@
import android.view.accessibility.Flags;
import com.android.internal.R;
-import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ScreenshotHelper;
@@ -561,16 +558,13 @@
}
private void handleAccessibilityButton() {
- AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
+ mA11yManager.notifyAccessibilityButtonClicked(
mDisplayTracker.getDefaultDisplayId());
}
private void handleAccessibilityButtonChooser() {
- final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
- intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
- mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
+ mA11yManager.notifyAccessibilityButtonLongClicked(
+ mDisplayTracker.getDefaultDisplayId());
}
private void handleAccessibilityShortcut() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 9b6501e..2f0ca6e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -482,6 +482,10 @@
} else { // mode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
mEditButton.setVisibility(View.VISIBLE);
mAllowDiagonalScrollingView.setVisibility(View.VISIBLE);
+ if (Flags.saveAndRestoreMagnificationSettingsButtons()) {
+ selectedButtonIndex =
+ windowMagnificationFrameSizePrefs.getIndexForCurrentDensity();
+ }
}
break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/packages/SystemUI/src/com/android/systemui/accessibility/data/model/CaptioningModel.kt
similarity index 76%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to packages/SystemUI/src/com/android/systemui/accessibility/data/model/CaptioningModel.kt
index 3c5beeb..4eb2274 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/model/CaptioningModel.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.systemui.accessibility.data.model
-parcelable BubbleBarLocation;
\ No newline at end of file
+data class CaptioningModel(
+ val isSystemAudioCaptioningUiEnabled: Boolean,
+ val isSystemAudioCaptioningEnabled: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
new file mode 100644
index 0000000..5414b62
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.data.repository
+
+import android.annotation.SuppressLint
+import android.view.accessibility.CaptioningManager
+import com.android.systemui.accessibility.data.model.CaptioningModel
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.user.utils.UserScopedService
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+interface CaptioningRepository {
+
+ /** Current state of Live Captions. */
+ val captioningModel: StateFlow<CaptioningModel?>
+
+ /** Sets [CaptioningModel.isSystemAudioCaptioningEnabled]. */
+ suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean)
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class CaptioningRepositoryImpl
+@Inject
+constructor(
+ private val userScopedCaptioningManagerProvider: UserScopedService<CaptioningManager>,
+ userRepository: UserRepository,
+ @Background private val backgroundCoroutineContext: CoroutineContext,
+ @Application coroutineScope: CoroutineScope,
+) : CaptioningRepository {
+
+ @SuppressLint("NonInjectedService") // this uses user-aware context
+ private val captioningManager: StateFlow<CaptioningManager?> =
+ userRepository.selectedUser
+ .map { userScopedCaptioningManagerProvider.forUser(it.userInfo.userHandle) }
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
+
+ override val captioningModel: StateFlow<CaptioningModel?> =
+ captioningManager
+ .filterNotNull()
+ .flatMapLatest { it.captioningModel() }
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
+
+ override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) {
+ withContext(backgroundCoroutineContext) {
+ captioningManager.value?.isSystemAudioCaptioningEnabled = isEnabled
+ }
+ }
+
+ private fun CaptioningManager.captioningModel(): Flow<CaptioningModel> {
+ return conflatedCallbackFlow {
+ val listener =
+ object : CaptioningManager.CaptioningChangeListener() {
+
+ override fun onSystemAudioCaptioningChanged(enabled: Boolean) {
+ trySend(Unit)
+ }
+
+ override fun onSystemAudioCaptioningUiChanged(enabled: Boolean) {
+ trySend(Unit)
+ }
+ }
+ addCaptioningChangeListener(listener)
+ awaitClose { removeCaptioningChangeListener(listener) }
+ }
+ .onStart { emit(Unit) }
+ .map {
+ CaptioningModel(
+ isSystemAudioCaptioningEnabled = isSystemAudioCaptioningEnabled,
+ isSystemAudioCaptioningUiEnabled = isSystemAudioCaptioningUiEnabled,
+ )
+ }
+ .flowOn(backgroundCoroutineContext)
+ }
+}
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..840edf4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractor.kt
@@ -0,0 +1,38 @@
+/*
+ * 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 com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class CaptioningInteractor @Inject constructor(private val repository: CaptioningRepository) {
+
+ val isSystemAudioCaptioningEnabled: Flow<Boolean> =
+ repository.captioningModel.filterNotNull().map { it.isSystemAudioCaptioningEnabled }
+
+ val isSystemAudioCaptioningUiEnabled: Flow<Boolean> =
+ repository.captioningModel.filterNotNull().map { it.isSystemAudioCaptioningUiEnabled }
+
+ suspend fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) {
+ repository.setIsSystemAudioCaptioningEnabled(enabled)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManager.kt b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManager.kt
index e1297d3..60d80ef 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManager.kt
@@ -15,7 +15,9 @@
*/
package com.android.systemui.accessibility.extradim
-import androidx.annotation.VisibleForTesting
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -28,25 +30,35 @@
@Inject
constructor(
private val extraDimDialogDelegateProvider: Provider<ExtraDimDialogDelegate>,
- private val mActivityStarter: ActivityStarter
+ private val mActivityStarter: ActivityStarter,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
) {
private var dialog: SystemUIDialog? = null
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
- fun dismissKeyguardIfNeededAndShowDialog() {
+ @JvmOverloads
+ fun dismissKeyguardIfNeededAndShowDialog(expandable: Expandable? = null) {
mActivityStarter.executeRunnableDismissingKeyguard(
- { showRemoveExtraDimShortcutsDialog() },
+ { showRemoveExtraDimShortcutsDialog(expandable) },
/* cancelAction= */ null,
/* dismissShade= */ false,
/* afterKeyguardGone= */ true,
- /* deferred= */ false
+ /* deferred= */ false,
)
}
/** Show the dialog for removing all Extra Dim shortcuts. */
- private fun showRemoveExtraDimShortcutsDialog() {
+ private fun showRemoveExtraDimShortcutsDialog(expandable: Expandable?) {
dialog?.dismiss()
- dialog = extraDimDialogDelegateProvider.get().createDialog()
- dialog!!.show()
+ val dialog2 = extraDimDialogDelegateProvider.get().createDialog()
+ dialog = dialog2
+
+ val controller =
+ expandable?.dialogTransitionController(
+ DialogCuj(com.android.internal.jank.Cuj.CUJ_SHADE_DIALOG_OPEN)
+ )
+
+ controller?.let {
+ dialogTransitionAnimator.show(dialog2, it, animateBackgroundBoundsChange = true)
+ } ?: dialog2.show()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index e4b7b7e..275147e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -21,11 +21,13 @@
import android.content.Context;
import android.hardware.display.DisplayManager;
+import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
import android.view.Display;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import androidx.annotation.MainThread;
@@ -68,6 +70,9 @@
private int mBtnMode;
private String mBtnTargets;
private boolean mIsKeyguardVisible;
+ private boolean mIsUserInInitialization;
+ @VisibleForTesting
+ Handler mHandler;
@VisibleForTesting
final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@@ -86,18 +91,14 @@
@Override
public void onUserSwitching(int userId) {
destroyFloatingMenu();
- }
-
- @Override
- public void onUserSwitchComplete(int userId) {
- mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
- mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
- mBtnTargets =
- mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
- handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
+ mIsUserInInitialization = true;
}
};
+ @VisibleForTesting
+ final UserInitializationCompleteCallback mUserInitializationCompleteCallback =
+ new UserInitializationCompleteCallback();
+
@Inject
public AccessibilityFloatingMenuController(Context context,
WindowManager windowManager,
@@ -109,7 +110,8 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecureSettings secureSettings,
DisplayTracker displayTracker,
- NavigationModeController navigationModeController) {
+ NavigationModeController navigationModeController,
+ Handler handler) {
mContext = context;
mWindowManager = windowManager;
mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
@@ -121,6 +123,7 @@
mSecureSettings = secureSettings;
mDisplayTracker = displayTracker;
mNavigationModeController = navigationModeController;
+ mHandler = handler;
mIsKeyguardVisible = false;
}
@@ -159,6 +162,8 @@
mAccessibilityButtonModeObserver.addListener(this);
mAccessibilityButtonTargetsObserver.addListener(this);
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
+ mAccessibilityManager.registerUserInitializationCompleteCallback(
+ mUserInitializationCompleteCallback);
}
/**
@@ -172,7 +177,7 @@
*/
private void handleFloatingMenuVisibility(boolean keyguardVisible,
@AccessibilityButtonMode int mode, String targets) {
- if (keyguardVisible) {
+ if (keyguardVisible || mIsUserInInitialization) {
destroyFloatingMenu();
return;
}
@@ -210,4 +215,18 @@
mFloatingMenu.hide();
mFloatingMenu = null;
}
+
+ class UserInitializationCompleteCallback
+ extends IUserInitializationCompleteCallback.Stub {
+ @Override
+ public void onUserInitializationComplete(int userId) {
+ mIsUserInInitialization = false;
+ mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
+ mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
+ mBtnTargets =
+ mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
+ mHandler.post(
+ () -> handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets));
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
index d718ae3..708f7f1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
@@ -30,8 +30,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Flags;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import java.util.Map;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
index c1b3962..13c1a45 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
@@ -39,9 +39,9 @@
import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.DismissCircleView
-import com.android.wm.shell.common.bubbles.DismissView
import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.DismissCircleView
+import com.android.wm.shell.shared.bubbles.DismissView
/**
* View that handles interactions between DismissCircleView and BubbleStackView.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index d62162b..7a674e2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -81,7 +81,7 @@
import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.bubbles.DismissViewUtils;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import java.lang.annotation.Retention;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index d08653c3..60edaae 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -52,7 +52,6 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
@@ -105,7 +104,8 @@
private final AudioManager mAudioManager;
private final LocalBluetoothProfileManager mProfileManager;
private final HapClientProfile mHapClientProfile;
- private final UiEventLogger mUiEventLogger;
+ private final HearingDevicesUiEventLogger mUiEventLogger;
+ private final int mLaunchSourceId;
private HearingDevicesListAdapter mDeviceListAdapter;
private HearingDevicesPresetsController mPresetsController;
private Context mApplicationContext;
@@ -153,20 +153,22 @@
public interface Factory {
/** Create a {@link HearingDevicesDialogDelegate} instance */
HearingDevicesDialogDelegate create(
- boolean showPairNewDevice);
+ boolean showPairNewDevice,
+ @HearingDevicesUiEventLogger.LaunchSourceId int launchSource);
}
@AssistedInject
public HearingDevicesDialogDelegate(
@Application Context applicationContext,
@Assisted boolean showPairNewDevice,
+ @Assisted @HearingDevicesUiEventLogger.LaunchSourceId int launchSourceId,
SystemUIDialog.Factory systemUIDialogFactory,
ActivityStarter activityStarter,
DialogTransitionAnimator dialogTransitionAnimator,
@Nullable LocalBluetoothManager localBluetoothManager,
@Main Handler handler,
AudioManager audioManager,
- UiEventLogger uiEventLogger) {
+ HearingDevicesUiEventLogger uiEventLogger) {
mApplicationContext = applicationContext;
mShowPairNewDevice = showPairNewDevice;
mSystemUIDialogFactory = systemUIDialogFactory;
@@ -178,6 +180,7 @@
mProfileManager = localBluetoothManager.getProfileManager();
mHapClientProfile = mProfileManager.getHapClientProfile();
mUiEventLogger = uiEventLogger;
+ mLaunchSourceId = launchSourceId;
}
@Override
@@ -191,7 +194,7 @@
@Override
public void onDeviceItemGearClicked(@NonNull DeviceItem deviceItem, @NonNull View view) {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK, mLaunchSourceId);
dismissDialogIfExists();
Intent intent = new Intent(ACTION_BLUETOOTH_DEVICE_DETAILS);
Bundle bundle = new Bundle();
@@ -207,15 +210,17 @@
CachedBluetoothDevice cachedBluetoothDevice = deviceItem.getCachedBluetoothDevice();
switch (deviceItem.getType()) {
case ACTIVE_MEDIA_BLUETOOTH_DEVICE, CONNECTED_BLUETOOTH_DEVICE -> {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT,
+ mLaunchSourceId);
cachedBluetoothDevice.disconnect();
}
case AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_SET_ACTIVE);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_SET_ACTIVE,
+ mLaunchSourceId);
cachedBluetoothDevice.setActive();
}
case SAVED_BLUETOOTH_DEVICE -> {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_CONNECT);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_CONNECT, mLaunchSourceId);
cachedBluetoothDevice.connect();
}
}
@@ -275,7 +280,7 @@
if (mLocalBluetoothManager == null) {
return;
}
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DIALOG_SHOW);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DIALOG_SHOW, mLaunchSourceId);
mPairButton = dialog.requireViewById(R.id.pair_new_device_button);
mDeviceList = dialog.requireViewById(R.id.device_list);
mPresetSpinner = dialog.requireViewById(R.id.preset_spinner);
@@ -363,7 +368,8 @@
mPresetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PRESET_SELECT);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PRESET_SELECT,
+ mLaunchSourceId);
mPresetsController.selectPreset(
mPresetsController.getAllPresetInfo().get(position).getIndex());
}
@@ -381,7 +387,7 @@
private void setupPairNewDeviceButton(SystemUIDialog dialog, @Visibility int visibility) {
if (visibility == VISIBLE) {
mPairButton.setOnClickListener(v -> {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR, mLaunchSourceId);
dismissDialogIfExists();
final Intent intent = new Intent(Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -485,7 +491,8 @@
final String name = intent.getComponent() != null
? intent.getComponent().flattenToString()
: intent.getPackage() + "/" + intent.getAction();
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_RELATED_TOOL_CLICK, 0, name);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_RELATED_TOOL_CLICK,
+ mLaunchSourceId, name);
dismissDialogIfExists();
mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
mDialogTransitionAnimator.createActivityTransitionController(view));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
index bc4cb45..3d24177 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
@@ -70,8 +70,10 @@
* Shows the dialog.
*
* @param expandable {@link Expandable} from which the dialog is shown.
+ * @param launchSourceId the id indicates where the dialog is launched from.
*/
- public void showDialog(Expandable expandable) {
+ public void showDialog(Expandable expandable,
+ @HearingDevicesUiEventLogger.LaunchSourceId int launchSourceId) {
if (mDialog != null) {
if (DEBUG) {
Log.d(TAG, "HearingDevicesDialog already showing. Destroy it first.");
@@ -91,7 +93,8 @@
});
pairedHearingDeviceCheckTask.addListener(() -> {
try {
- mDialog = mDialogFactory.create(!pairedHearingDeviceCheckTask.get()).createDialog();
+ mDialog = mDialogFactory.create(!pairedHearingDeviceCheckTask.get(),
+ launchSourceId).createDialog();
if (expandable != null) {
DialogTransitionAnimator.Controller controller =
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
index 6a34d19..02e65fd 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
@@ -16,6 +16,8 @@
package com.android.systemui.accessibility.hearingaid;
+import static com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.LAUNCH_SOURCE_A11Y;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -46,7 +48,7 @@
}
if (ACTION.equals(intent.getAction())) {
- mDialogManager.showDialog(/* view= */ null);
+ mDialogManager.showDialog(/* expandable= */ null, LAUNCH_SOURCE_A11Y);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java
deleted file mode 100644
index 3fbe56e..0000000
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java
+++ /dev/null
@@ -1,51 +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.accessibility.hearingaid;
-
-import com.android.internal.logging.UiEvent;
-import com.android.internal.logging.UiEventLogger;
-
-public enum HearingDevicesUiEvent implements UiEventLogger.UiEventEnum {
-
- @UiEvent(doc = "Hearing devices dialog is shown")
- HEARING_DEVICES_DIALOG_SHOW(1848),
- @UiEvent(doc = "Pair new device")
- HEARING_DEVICES_PAIR(1849),
- @UiEvent(doc = "Connect to the device")
- HEARING_DEVICES_CONNECT(1850),
- @UiEvent(doc = "Disconnect from the device")
- HEARING_DEVICES_DISCONNECT(1851),
- @UiEvent(doc = "Set the device as active device")
- HEARING_DEVICES_SET_ACTIVE(1852),
- @UiEvent(doc = "Click on the device gear to enter device detail page")
- HEARING_DEVICES_GEAR_CLICK(1853),
- @UiEvent(doc = "Select a preset from preset spinner")
- HEARING_DEVICES_PRESET_SELECT(1854),
- @UiEvent(doc = "Click on related tool")
- HEARING_DEVICES_RELATED_TOOL_CLICK(1856);
-
- private final int mId;
-
- HearingDevicesUiEvent(int id) {
- mId = id;
- }
-
- @Override
- public int getId() {
- return mId;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
new file mode 100644
index 0000000..9e77b02
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.hearingaid
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class HearingDevicesUiEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "Hearing devices dialog is shown") HEARING_DEVICES_DIALOG_SHOW(1848),
+ @UiEvent(doc = "Pair new device") HEARING_DEVICES_PAIR(1849),
+ @UiEvent(doc = "Connect to the device") HEARING_DEVICES_CONNECT(1850),
+ @UiEvent(doc = "Disconnect from the device") HEARING_DEVICES_DISCONNECT(1851),
+ @UiEvent(doc = "Set the device as active device") HEARING_DEVICES_SET_ACTIVE(1852),
+ @UiEvent(doc = "Click on the device gear to enter device detail page")
+ HEARING_DEVICES_GEAR_CLICK(1853),
+ @UiEvent(doc = "Select a preset from preset spinner") HEARING_DEVICES_PRESET_SELECT(1854),
+ @UiEvent(doc = "Click on related tool") HEARING_DEVICES_RELATED_TOOL_CLICK(1856);
+
+ override fun getId(): Int = this.id
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEventLogger.kt
new file mode 100644
index 0000000..0b32cfc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEventLogger.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.hearingaid
+
+import android.annotation.IntDef
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class HearingDevicesUiEventLogger @Inject constructor(private val uiEventLogger: UiEventLogger) {
+
+ /** Logs the given event */
+ fun log(event: UiEventLogger.UiEventEnum, launchSourceId: Int) {
+ log(event, launchSourceId, null)
+ }
+
+ fun log(event: UiEventLogger.UiEventEnum, launchSourceId: Int, pkgName: String?) {
+ uiEventLogger.log(event, launchSourceId, pkgName)
+ }
+
+ /**
+ * The possible launch source of hearing devices dialog
+ *
+ * @hide
+ */
+ @IntDef(LAUNCH_SOURCE_UNKNOWN, LAUNCH_SOURCE_A11Y, LAUNCH_SOURCE_QS_TILE)
+ annotation class LaunchSourceId
+
+ companion object {
+ const val LAUNCH_SOURCE_UNKNOWN = 0
+ const val LAUNCH_SOURCE_A11Y = 1 // launch from AccessibilityManagerService
+ const val LAUNCH_SOURCE_QS_TILE = 2
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index 03f282e..cd9efaf 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -19,6 +19,7 @@
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.ColorCorrectionTile
import com.android.systemui.qs.tiles.ColorInversionTile
@@ -120,42 +121,42 @@
@IntoMap
@StringKey(COLOR_CORRECTION_TILE_SPEC)
fun provideColorCorrectionAvailabilityInteractor(
- impl: ColorCorrectionTileDataInteractor
+ impl: ColorCorrectionTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(COLOR_INVERSION_TILE_SPEC)
fun provideColorInversionAvailabilityInteractor(
- impl: ColorCorrectionTileDataInteractor
+ impl: ColorCorrectionTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(FONT_SCALING_TILE_SPEC)
fun provideFontScalingAvailabilityInteractor(
- impl: FontScalingTileDataInteractor
+ impl: FontScalingTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(REDUCE_BRIGHTNESS_TILE_SPEC)
fun provideReduceBrightnessAvailabilityInteractor(
- impl: ReduceBrightColorsTileDataInteractor
+ impl: ReduceBrightColorsTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(ONE_HANDED_TILE_SPEC)
fun provideOneHandedAvailabilityInteractor(
- impl: OneHandedModeTileDataInteractor
+ impl: OneHandedModeTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(NIGHT_DISPLAY_TILE_SPEC)
fun provideNightDisplayAvailabilityInteractor(
- impl: NightDisplayTileDataInteractor
+ impl: NightDisplayTileDataInteractor
): QSTileAvailabilityInteractor
companion object {
@@ -165,6 +166,7 @@
const val REDUCE_BRIGHTNESS_TILE_SPEC = "reduce_brightness"
const val ONE_HANDED_TILE_SPEC = "onehanded"
const val NIGHT_DISPLAY_TILE_SPEC = "night"
+ const val HEARING_DEVICES_TILE_SPEC = "hearing_devices"
@Provides
@IntoMap
@@ -178,6 +180,7 @@
labelRes = R.string.quick_settings_color_correction_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
)
/** Inject ColorCorrectionTile into tileViewModelMap in QSModule */
@@ -209,6 +212,7 @@
labelRes = R.string.quick_settings_inversion_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
)
/** Inject ColorInversionTile into tileViewModelMap in QSModule */
@@ -240,6 +244,7 @@
labelRes = R.string.quick_settings_font_scaling_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/** Inject FontScaling Tile into tileViewModelMap in QSModule */
@@ -271,6 +276,22 @@
labelRes = com.android.internal.R.string.reduce_bright_colors_feature_name,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
+ )
+
+ @Provides
+ @IntoMap
+ @StringKey(HEARING_DEVICES_TILE_SPEC)
+ fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_hearing_devices_icon,
+ labelRes = R.string.quick_settings_hearing_devices_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
)
/**
@@ -307,6 +328,7 @@
labelRes = R.string.quick_settings_onehanded_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
)
/** Inject One Handed Mode Tile into tileViewModelMap in QSModule. */
@@ -340,6 +362,7 @@
labelRes = R.string.quick_settings_night_display_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
index b0314d8..476d54b 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
@@ -32,5 +32,6 @@
interface AmbientModule {
companion object {
const val TOUCH_HANDLERS = "touch_handlers"
+ const val LOGGING_NAME = "logging_name"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 1be6f9e..0898134 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static com.android.systemui.ambient.dagger.AmbientModule.LOGGING_NAME;
import static com.android.systemui.shared.Flags.bouncerAreaExclusion;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -44,6 +45,9 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.Logger;
+import com.android.systemui.log.dagger.CommunalTouchLog;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.util.display.DisplayHelper;
@@ -62,6 +66,8 @@
import java.util.stream.Collectors;
import javax.inject.Inject;
+import javax.inject.Named;
+
/**
* {@link TouchMonitor} is responsible for monitoring touches and gestures over the
@@ -73,6 +79,7 @@
// This executor is used to protect {@code mActiveTouchSessions} from being modified
// concurrently. Any operation that adds or removes values should use this executor.
public String TAG = "DreamOverlayTouchMonitor";
+ private final Logger mLogger;
private final Executor mMainExecutor;
private final Executor mBackgroundExecutor;
@@ -116,6 +123,11 @@
TouchSessionImpl touchSessionImpl) {
return CallbackToFutureAdapter.getFuture(completer -> {
mMainExecutor.execute(() -> {
+ mLogger.i(msg -> "Session popped, hashCode: " + msg.getInt1(), msg -> {
+ msg.setInt1(touchSessionImpl.hashCode());
+ return kotlin.Unit.INSTANCE;
+ });
+
if (mActiveTouchSessions.remove(touchSessionImpl)) {
touchSessionImpl.onRemoved();
@@ -269,6 +281,7 @@
* When invoked, instantiates a new {@link InputSession} to monitor touch events.
*/
private void startMonitoring() {
+ mLogger.i("startMonitoring(): monitoring started");
stopMonitoring(true);
if (bouncerAreaExclusion()) {
@@ -322,6 +335,12 @@
}
if (!mActiveTouchSessions.isEmpty() && !force) {
+ mLogger.i(msg -> "stopMonitoring(): waiting for sessions to end: " + msg.getStr1(),
+ msg -> {
+ msg.setStr1(mActiveTouchSessions.stream().map(Object::hashCode).map(
+ Object::toString).collect(Collectors.joining(",")));
+ return kotlin.Unit.INSTANCE;
+ });
mStopMonitoringPending = true;
return;
}
@@ -341,6 +360,8 @@
mCurrentInputSession.dispose();
mCurrentInputSession = null;
mStopMonitoringPending = false;
+
+ mLogger.i("stopMonitoring(): monitoring finished");
}
@@ -405,12 +426,29 @@
// created so the
// final session is correct.
sessionMap.forEach((dreamTouchHandler, touchSession)
- -> dreamTouchHandler.onSessionStart(touchSession));
+ -> {
+ if (ev instanceof MotionEvent motionEvent) {
+ int x = Math.round(motionEvent.getX());
+ int y = Math.round(motionEvent.getY());
+ mLogger.i(
+ msg -> "Session start, handler: " + msg.getStr1() + ", x: "
+ + msg.getLong1() + ", y: " + msg.getLong2()
+ + ", hashCode: " + msg.getInt1(), msg -> {
+ msg.setStr1(
+ dreamTouchHandler.getClass().getSimpleName());
+ msg.setLong1(x);
+ msg.setLong2(y);
+ msg.setInt1(touchSession.hashCode());
+ return kotlin.Unit.INSTANCE;
+ });
+ }
+ dreamTouchHandler.onSessionStart(touchSession);
+ });
}
// Find active sessions and invoke on InputEvent.
mActiveTouchSessions.stream()
- .map(touchSessionStack -> touchSessionStack.getEventListeners())
+ .map(TouchSessionImpl::getEventListeners)
.flatMap(Collection::stream)
.forEach(inputEventListener -> inputEventListener.onInputEvent(ev));
}
@@ -526,6 +564,8 @@
* returned.
* @param handlers This set represents the {@link TouchHandler} instances that will
* participate in touch handling.
+ * @param loggingName Identifying string for this {@link TouchMonitor} that will be used
+ * when logging to {@link CommunalTouchLog}.
*/
@Inject
public TouchMonitor(
@@ -537,7 +577,9 @@
ConfigurationInteractor configurationInteractor,
Set<TouchHandler> handlers,
IWindowManager windowManagerService,
- @DisplayId int displayId) {
+ @DisplayId int displayId,
+ @Named(LOGGING_NAME) String loggingName,
+ @CommunalTouchLog LogBuffer logBuffer) {
mDisplayId = displayId;
mHandlers = handlers;
mInputSessionFactory = inputSessionFactory;
@@ -547,6 +589,7 @@
mDisplayHelper = displayHelper;
mWindowManagerService = windowManagerService;
mConfigurationInteractor = configurationInteractor;
+ mLogger = new Logger(logBuffer, loggingName + ":TouchMonitor");
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt
index 390e53b..ba552c3 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt
@@ -16,6 +16,7 @@
package com.android.systemui.ambient.touch.dagger
import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.ambient.dagger.AmbientModule.Companion.LOGGING_NAME
import com.android.systemui.ambient.dagger.AmbientModule.Companion.TOUCH_HANDLERS
import com.android.systemui.ambient.touch.TouchHandler
import com.android.systemui.ambient.touch.TouchMonitor
@@ -36,7 +37,8 @@
@BindsInstance lifecycleOwner: LifecycleOwner,
@BindsInstance
@Named(TOUCH_HANDLERS)
- touchHandlers: Set<@JvmSuppressWildcards TouchHandler>
+ touchHandlers: Set<@JvmSuppressWildcards TouchHandler>,
+ @BindsInstance @Named(LOGGING_NAME) loggingName: String,
): AmbientTouchComponent
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 468737d..732a90d 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -32,11 +32,11 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.onSubscriberAdded
@@ -254,7 +254,7 @@
override val hasLockoutOccurred: StateFlow<Boolean> = _hasLockoutOccurred.asStateFlow()
init {
- if (SceneContainerFlag.isEnabled) {
+ if (ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
// Hydrate failedAuthenticationAttempts initially and whenever the selected user
// changes.
applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index fcba425..3080e19 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.authentication.domain.interactor
-import android.app.admin.flags.Flags
import android.os.UserHandle
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
@@ -289,12 +288,7 @@
private suspend fun getWipeTarget(): WipeTarget {
// Check which profile has the strictest policy for failed authentication attempts.
val userToBeWiped = repository.getProfileWithMinFailedUnlockAttemptsForWipe()
- val primaryUser =
- if (Flags.headlessSingleUserFixes()) {
- selectedUserInteractor.getMainUserId() ?: UserHandle.USER_SYSTEM
- } else {
- UserHandle.USER_SYSTEM
- }
+ val primaryUser = selectedUserInteractor.getMainUserId() ?: UserHandle.USER_SYSTEM
return when (userToBeWiped) {
selectedUserInteractor.getSelectedUserId() ->
if (userToBeWiped == primaryUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
index 8a9a322..831bc1d 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
@@ -2,6 +2,7 @@
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.BatterySaverTile
import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
@@ -33,7 +34,7 @@
@IntoMap
@StringKey(BATTERY_SAVER_TILE_SPEC)
fun provideBatterySaverAvailabilityInteractor(
- impl: BatterySaverTileDataInteractor
+ impl: BatterySaverTileDataInteractor
): QSTileAvailabilityInteractor
companion object {
@@ -51,6 +52,7 @@
labelRes = R.string.battery_detail_switch_title,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject BatterySaverTile into tileViewModelMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 970fdea..69ab976 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -78,6 +78,8 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import kotlin.Lazy;
import kotlinx.coroutines.CoroutineScope;
@@ -157,6 +159,8 @@
private final @Background DelayableExecutor mBackgroundExecutor;
+ private final MSDLPlayer mMSDLPlayer;
+
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason;
// HAT received from LockSettingsService when credential is verified.
@@ -292,7 +296,8 @@
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull @Background DelayableExecutor bgExecutor,
@NonNull VibratorHelper vibratorHelper,
- Lazy<ViewCapture> lazyViewCapture) {
+ Lazy<ViewCapture> lazyViewCapture,
+ @NonNull MSDLPlayer msdlPlayer) {
super(config.mContext);
mConfig = config;
@@ -309,6 +314,7 @@
.getDimension(R.dimen.biometric_dialog_animation_translation_offset);
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
mBiometricCallback = new BiometricCallback();
+ mMSDLPlayer = msdlPlayer;
final BiometricModalities biometricModalities = new BiometricModalities(
Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
@@ -379,7 +385,7 @@
getJankListener(mLayout, TRANSIT,
BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
- vibratorHelper);
+ vibratorHelper, mMSDLPlayer);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 097ab72..b39aae9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -89,6 +89,8 @@
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import dagger.Lazy;
import kotlin.Unit;
@@ -183,6 +185,7 @@
private final @Background DelayableExecutor mBackgroundExecutor;
private final DisplayInfo mCachedDisplayInfo = new DisplayInfo();
@NonNull private final VibratorHelper mVibratorHelper;
+ @NonNull private final MSDLPlayer mMSDLPlayer;
private final kotlin.Lazy<ViewCapture> mLazyViewCapture;
@@ -742,7 +745,8 @@
@Background DelayableExecutor bgExecutor,
@NonNull UdfpsUtils udfpsUtils,
@NonNull VibratorHelper vibratorHelper,
- Lazy<ViewCapture> daggerLazyViewCapture) {
+ Lazy<ViewCapture> daggerLazyViewCapture,
+ @NonNull MSDLPlayer msdlPlayer) {
mContext = context;
mExecution = execution;
mUserManager = userManager;
@@ -764,6 +768,7 @@
mUdfpsUtils = udfpsUtils;
mApplicationCoroutineScope = applicationCoroutineScope;
mVibratorHelper = vibratorHelper;
+ mMSDLPlayer = msdlPlayer;
mLogContextInteractor = logContextInteractor;
mPromptSelectorInteractor = promptSelectorInteractorProvider;
@@ -1327,7 +1332,7 @@
wakefulnessLifecycle, userManager, lockPatternUtils,
mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
mCredentialViewModelProvider, bgExecutor, mVibratorHelper,
- mLazyViewCapture);
+ mLazyViewCapture, mMSDLPlayer);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
index ec3fd9f..7ecbb88 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
@@ -25,6 +25,8 @@
import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor
+import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl
import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
@@ -46,6 +48,12 @@
@Binds
@SysUISingleton
+ fun providesSideFpsOverlayInteractor(
+ impl: SideFpsOverlayInteractorImpl
+ ): SideFpsOverlayInteractor
+
+ @Binds
+ @SysUISingleton
fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt
new file mode 100644
index 0000000..10c3483
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.biometrics.domain.interactor
+
+import android.util.Log
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.onEach
+
+/** Encapsulates business logic for showing and hiding the side fingerprint sensor indicator. */
+interface SideFpsOverlayInteractor {
+ /** Whether the side fingerprint sensor indicator is currently showing. */
+ val isShowing: Flow<Boolean>
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class SideFpsOverlayInteractorImpl
+@Inject
+constructor(
+ biometricStatusInteractor: BiometricStatusInteractor,
+ displayStateInteractor: DisplayStateInteractor,
+ deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
+ sfpsSensorInteractor: SideFpsSensorInteractor,
+ // TODO(b/365182034): add progress bar input when rest to unlock feature is implemented
+) : SideFpsOverlayInteractor {
+ private val sfpsOverlayEnabled: Flow<Boolean> =
+ sfpsSensorInteractor.isAvailable.sample(displayStateInteractor.isInRearDisplayMode) {
+ isAvailable: Boolean,
+ isInRearDisplayMode: Boolean ->
+ isAvailable && !isInRearDisplayMode
+ }
+
+ private val showSideFpsOverlay: Flow<Boolean> =
+ combine(
+ biometricStatusInteractor.sfpsAuthenticationReason,
+ deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry,
+ // TODO(b/365182034): add progress bar input when rest to unlock feature is implemented
+ ) { systemServerAuthReason, showIndicatorForDeviceEntry ->
+ Log.d(
+ TAG,
+ "systemServerAuthReason = $systemServerAuthReason, " +
+ "showIndicatorForDeviceEntry = $showIndicatorForDeviceEntry, "
+ )
+ systemServerAuthReason != NotRunning || showIndicatorForDeviceEntry
+ }
+
+ override val isShowing: Flow<Boolean> =
+ sfpsOverlayEnabled
+ .flatMapLatest { sfpsOverlayEnabled ->
+ if (!sfpsOverlayEnabled) {
+ flowOf(false)
+ } else {
+ showSideFpsOverlay
+ }
+ }
+ .onEach { Log.d(TAG, "isShowing: $it") }
+
+ companion object {
+ private const val TAG = "SideFpsOverlayInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 0b440ad..e7e8d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -25,7 +25,6 @@
import android.hardware.biometrics.Flags
import android.hardware.face.FaceManager
import android.util.Log
-import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.View
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
@@ -59,6 +58,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
+import com.google.android.msdl.domain.MSDLPlayer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
@@ -83,6 +83,7 @@
legacyCallback: Spaghetti.Callback,
applicationScope: CoroutineScope,
vibratorHelper: VibratorHelper,
+ msdlPlayer: MSDLPlayer,
): Spaghetti {
val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!
@@ -434,21 +435,27 @@
// Play haptics
launch {
viewModel.hapticsToPlay.collect { haptics ->
- if (haptics.hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
- if (haptics.flag != null) {
- vibratorHelper.performHapticFeedback(
- view,
- haptics.hapticFeedbackConstant,
- haptics.flag,
- )
- } else {
- vibratorHelper.performHapticFeedback(
- view,
- haptics.hapticFeedbackConstant,
- )
+ when (haptics) {
+ is PromptViewModel.HapticsToPlay.HapticConstant -> {
+ if (haptics.flag != null) {
+ vibratorHelper.performHapticFeedback(
+ view,
+ haptics.constant,
+ haptics.flag,
+ )
+ } else {
+ vibratorHelper.performHapticFeedback(
+ view,
+ haptics.constant,
+ )
+ }
}
- viewModel.clearHaptics()
+ is PromptViewModel.HapticsToPlay.MSDL -> {
+ msdlPlayer.playToken(haptics.token, haptics.properties)
+ }
+ is PromptViewModel.HapticsToPlay.None -> {}
}
+ viewModel.clearHaptics()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 85c3ae3..d055731 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -18,13 +18,11 @@
import android.animation.Animator
import android.animation.AnimatorSet
-import android.animation.ValueAnimator
import android.graphics.Outline
import android.graphics.Rect
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.util.TypedValue
-import android.view.Surface
import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
@@ -160,16 +158,13 @@
fun setVisibilities(hideSensorIcon: Boolean, size: PromptSize) {
viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
if (hideSensorIcon) {
smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
}
}
@@ -413,13 +408,12 @@
ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
)
- TransitionManager.beginDelayedTransition(view, autoTransition)
-
if (position.isLeft) {
flipConstraintSet.applyTo(view)
} else {
mediumConstraintSet.applyTo(view)
}
+ TransitionManager.beginDelayedTransition(view, autoTransition)
}
size.isMedium -> {
if (position.isLeft) {
@@ -428,14 +422,18 @@
mediumConstraintSet.applyTo(view)
}
}
- size.isLarge && currentSize.isMedium -> {
+ size.isLarge -> {
val autoTransition = AutoTransition()
autoTransition.setDuration(
- ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
+ if (currentSize.isSmall) {
+ ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
+ } else {
+ ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
+ }
)
- TransitionManager.beginDelayedTransition(view, autoTransition)
largeConstraintSet.applyTo(view)
+ TransitionManager.beginDelayedTransition(view, autoTransition)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 9578da4..9fe1dc5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -33,89 +33,44 @@
import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardPINView
import com.android.systemui.CoreStartable
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
-import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
-import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning
+import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.LottieCallback
import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
-import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
/** Binds the side fingerprint sensor indicator view to [SideFpsOverlayViewModel]. */
-@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class SideFpsOverlayViewBinder
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
@Application private val applicationContext: Context,
- private val biometricStatusInteractor: Lazy<BiometricStatusInteractor>,
- private val displayStateInteractor: Lazy<DisplayStateInteractor>,
- private val deviceEntrySideFpsOverlayInteractor: Lazy<DeviceEntrySideFpsOverlayInteractor>,
private val layoutInflater: Lazy<LayoutInflater>,
- private val sideFpsProgressBarViewModel: Lazy<SideFpsProgressBarViewModel>,
- private val sfpsSensorInteractor: Lazy<SideFpsSensorInteractor>,
+ private val sideFpsOverlayInteractor: Lazy<SideFpsOverlayInteractor>,
+ private val sideFpsOverlayViewModel: Lazy<SideFpsOverlayViewModel>,
private val windowManager: Lazy<WindowManager>
) : CoreStartable {
+ private var overlayView: View? = null
override fun start() {
- applicationScope
- .launch {
- sfpsSensorInteractor.get().isAvailable.collect { isSfpsAvailable ->
- if (isSfpsAvailable) {
- combine(
- biometricStatusInteractor.get().sfpsAuthenticationReason,
- deviceEntrySideFpsOverlayInteractor
- .get()
- .showIndicatorForDeviceEntry,
- sideFpsProgressBarViewModel.get().isVisible,
- ::Triple
- )
- .sample(displayStateInteractor.get().isInRearDisplayMode, ::Pair)
- .collect { (combinedFlows, isInRearDisplayMode: Boolean) ->
- val (
- systemServerAuthReason,
- showIndicatorForDeviceEntry,
- progressBarIsVisible) =
- combinedFlows
- Log.d(
- TAG,
- "systemServerAuthReason = $systemServerAuthReason, " +
- "showIndicatorForDeviceEntry = " +
- "$showIndicatorForDeviceEntry, " +
- "progressBarIsVisible = $progressBarIsVisible"
- )
- if (!isInRearDisplayMode) {
- if (progressBarIsVisible) {
- hide()
- } else if (systemServerAuthReason != NotRunning) {
- show()
- } else if (showIndicatorForDeviceEntry) {
- show()
- } else {
- hide()
- }
- }
- }
- }
+ applicationScope.launch {
+ sideFpsOverlayInteractor.get().isShowing.collect { isShowing: Boolean ->
+ if (isShowing) {
+ show()
+ } else {
+ hide()
}
}
+ }
}
- private var overlayView: View? = null
-
/** Show the side fingerprint sensor indicator */
private fun show() {
if (overlayView?.isAttachedToWindow == true) {
@@ -125,17 +80,10 @@
)
return
}
-
overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false)
-
- val overlayViewModel =
- SideFpsOverlayViewModel(
- applicationContext,
- deviceEntrySideFpsOverlayInteractor.get(),
- displayStateInteractor.get(),
- sfpsSensorInteractor.get(),
- )
+ val overlayViewModel = sideFpsOverlayViewModel.get()
bind(overlayView!!, overlayViewModel, windowManager.get())
+
overlayView!!.visibility = View.INVISIBLE
Log.d(TAG, "show(): adding overlayView $overlayView")
windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
@@ -161,6 +109,20 @@
companion object {
private const val TAG = "SideFpsOverlayViewBinder"
+ private val accessibilityDelegate =
+ object : View.AccessibilityDelegate() {
+ override fun dispatchPopulateAccessibilityEvent(
+ host: View,
+ event: AccessibilityEvent
+ ): Boolean {
+ return if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ true
+ } else {
+ super.dispatchPopulateAccessibilityEvent(host, event)
+ }
+ }
+ }
+
/** Binds overlayView (side fingerprint sensor indicator view) to SideFpsOverlayViewModel */
fun bind(
overlayView: View,
@@ -184,24 +146,7 @@
overlayShowAnimator.start()
- it.setAccessibilityDelegate(
- object : View.AccessibilityDelegate() {
- override fun dispatchPopulateAccessibilityEvent(
- host: View,
- event: AccessibilityEvent
- ): Boolean {
- return if (
- event.getEventType() ===
- android.view.accessibility.AccessibilityEvent
- .TYPE_WINDOW_STATE_CHANGED
- ) {
- true
- } else {
- super.dispatchPopulateAccessibilityEvent(host, event)
- }
- }
- }
- )
+ it.accessibilityDelegate = accessibilityDelegate
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 25d43d9..85f221f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -35,7 +35,9 @@
import android.util.RotationUtils
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
+import com.android.keyguard.AuthInteractionProperties
import com.android.launcher3.icons.IconProvider
+import com.android.systemui.Flags.msdlFeedback
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.Utils.isSystem
@@ -53,6 +55,8 @@
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.combine
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.InteractionProperties
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
@@ -74,11 +78,11 @@
class PromptViewModel
@Inject
constructor(
- displayStateInteractor: DisplayStateInteractor,
+ private val displayStateInteractor: DisplayStateInteractor,
private val promptSelectorInteractor: PromptSelectorInteractor,
@Application private val context: Context,
- private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
- private val biometricStatusInteractor: BiometricStatusInteractor,
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ biometricStatusInteractor: BiometricStatusInteractor,
private val udfpsUtils: UdfpsUtils,
private val iconProvider: IconProvider,
private val activityTaskManager: ActivityTaskManager,
@@ -131,11 +135,13 @@
R.dimen.biometric_prompt_landscape_medium_horizontal_padding
)
+ val currentRotation: StateFlow<DisplayRotation> = displayStateInteractor.currentRotation
+
val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
udfpsOverlayInteractor.udfpsOverlayParams
private val udfpsSensorBounds: Flow<Rect> =
- combine(udfpsOverlayParams, displayStateInteractor.currentRotation) { params, rotation ->
+ combine(udfpsOverlayParams, currentRotation) { params, rotation ->
val rotatedBounds = Rect(params.sensorBounds)
RotationUtils.rotateBounds(
rotatedBounds,
@@ -245,8 +251,9 @@
private val _forceLargeSize = MutableStateFlow(false)
private val _forceMediumSize = MutableStateFlow(false)
- private val _hapticsToPlay =
- MutableStateFlow(HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, /* flag= */ null))
+ private val authInteractionProperties = AuthInteractionProperties()
+ private val _hapticsToPlay: MutableStateFlow<HapticsToPlay> =
+ MutableStateFlow(HapticsToPlay.None)
/** Event fired to the view indicating a [HapticsToPlay] */
val hapticsToPlay = _hapticsToPlay.asStateFlow()
@@ -257,7 +264,7 @@
_forceLargeSize,
promptKind,
displayStateInteractor.isLargeScreen,
- displayStateInteractor.currentRotation,
+ currentRotation,
modalities
) { forceLarge, promptKind, isLargeScreen, rotation, modalities ->
when {
@@ -449,7 +456,7 @@
/** Padding for prompt UI elements */
val promptPadding: Flow<Rect> =
- combine(size, displayStateInteractor.currentRotation) { size, rotation ->
+ combine(size, currentRotation) { size, rotation ->
if (size != PromptSize.LARGE) {
val navBarInsets = Utils.getNavbarInsets(context)
if (rotation == DisplayRotation.ROTATION_90) {
@@ -939,26 +946,52 @@
}
private fun vibrateOnSuccess() {
- _hapticsToPlay.value =
- HapticsToPlay(
- HapticFeedbackConstants.CONFIRM,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
- )
+ val haptics =
+ if (msdlFeedback()) {
+ HapticsToPlay.MSDL(MSDLToken.UNLOCK, authInteractionProperties)
+ } else {
+ HapticsToPlay.HapticConstant(
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+ flag = null,
+ )
+ }
+ _hapticsToPlay.value = haptics
}
private fun vibrateOnError() {
- _hapticsToPlay.value =
- HapticsToPlay(
- HapticFeedbackConstants.REJECT,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
- )
+ val haptics =
+ if (msdlFeedback()) {
+ HapticsToPlay.MSDL(MSDLToken.FAILURE, authInteractionProperties)
+ } else {
+ HapticsToPlay.HapticConstant(
+ HapticFeedbackConstants.BIOMETRIC_REJECT,
+ flag = null,
+ )
+ }
+ _hapticsToPlay.value = haptics
}
/** Clears the [hapticsToPlay] variable by setting its constant to the NO_HAPTICS default. */
fun clearHaptics() {
- _hapticsToPlay.update { previous ->
- HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, previous.flag)
- }
+ _hapticsToPlay.update { HapticsToPlay.None }
+ }
+
+ /** The state of haptic feedback to play. */
+ sealed interface HapticsToPlay {
+ /**
+ * Haptics using [HapticFeedbackConstants]. It is composed by a [HapticFeedbackConstants]
+ * and a [HapticFeedbackConstants] flag.
+ */
+ data class HapticConstant(val constant: Int, val flag: Int?) : HapticsToPlay
+
+ /**
+ * Haptics using MSDL feedback. It is composed by a [MSDLToken] and optional
+ * [InteractionProperties]
+ */
+ data class MSDL(val token: MSDLToken, val properties: InteractionProperties?) :
+ HapticsToPlay
+
+ data object None : HapticsToPlay
}
companion object {
@@ -1095,9 +1128,3 @@
val isStarted: Boolean
get() = this == Normal || this == Delayed
}
-
-/**
- * The state of haptic feedback to play. It is composed by a [HapticFeedbackConstants] and a
- * [HapticFeedbackConstants] flag.
- */
-data class HapticsToPlay(val hapticFeedbackConstant: Int, val flag: Int?)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
index c2a4ee3..7c1984e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -147,8 +147,7 @@
_lottieBounds,
sensorLocation,
displayRotation,
- ) { bounds: Rect?, sensorLocation: SideFpsSensorLocation, displayRotation: DisplayRotation
- ->
+ ) { _: Rect?, sensorLocation: SideFpsSensorLocation, _: DisplayRotation ->
val topLeft = Point(sensorLocation.left, sensorLocation.top)
defaultOverlayViewParams.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
index f36ef66..8b5a09b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
@@ -34,10 +34,14 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.doze.DozeLogger
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.EmergencyDialerConstants
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -69,6 +73,7 @@
private val emergencyDialerIntentFactory: EmergencyDialerIntentFactory,
private val metricsLogger: MetricsLogger,
private val dozeLogger: DozeLogger,
+ private val sceneInteractor: Lazy<SceneInteractor>,
) {
/** The bouncer action button. If `null`, the button should not be shown. */
val actionButton: Flow<BouncerActionButtonModel?> =
@@ -158,14 +163,17 @@
}
private fun prepareToPerformAction() {
- // TODO(b/308001302): Trigger occlusion and resetting bouncer state.
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.get().changeScene(Scenes.Lockscreen, "Bouncer action button clicked")
+ }
+
metricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL)
activityTaskManager.stopSystemLockTaskMode()
}
@SuppressLint("MissingPermission")
private fun returnToCall() {
- telecomManager?.showInCallScreen(/* showDialpad = */ false)
+ telecomManager?.showInCallScreen(/* showDialpad= */ false)
}
private val <T> Flow<T>.asUnitFlow: Flow<Unit>
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
index 62ef365..d7a4863b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
@@ -17,18 +17,21 @@
package com.android.systemui.bouncer.shared.flag
import com.android.systemui.Flags
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import dagger.Module
-import dagger.Provides
-interface ComposeBouncerFlags {
+object ComposeBouncerFlags {
+
+ /** @see [isComposeBouncerOrSceneContainerEnabled] */
+ val isEnabled: Boolean
+ get() = isComposeBouncerOrSceneContainerEnabled()
/**
* Returns `true` if the Compose bouncer is enabled or if the scene container framework is
* enabled; `false` otherwise.
*/
- fun isComposeBouncerOrSceneContainerEnabled(): Boolean
+ fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
+ return SceneContainerFlag.isEnabled || Flags.composeBouncer()
+ }
/**
* Returns `true` if only compose bouncer is enabled and scene container framework is not
@@ -39,30 +42,7 @@
"that includes compose bouncer in legacy keyguard.",
replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
)
- fun isOnlyComposeBouncerEnabled(): Boolean
-}
-
-class ComposeBouncerFlagsImpl() : ComposeBouncerFlags {
-
- override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
- return SceneContainerFlag.isEnabled || Flags.composeBouncer()
- }
-
- @Deprecated(
- "Avoid using this, this is meant to be used only by the glue code " +
- "that includes compose bouncer in legacy keyguard.",
- replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
- )
- override fun isOnlyComposeBouncerEnabled(): Boolean {
+ fun isOnlyComposeBouncerEnabled(): Boolean {
return !SceneContainerFlag.isEnabled && Flags.composeBouncer()
}
}
-
-@Module
-object ComposeBouncerFlagsModule {
- @Provides
- @SysUISingleton
- fun impl(): ComposeBouncerFlags {
- return ComposeBouncerFlagsImpl()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index ad93a25..49dadce 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -2,13 +2,11 @@
import android.view.ViewGroup
import com.android.keyguard.KeyguardMessageAreaController
-import com.android.keyguard.ViewMediatorCallback
import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.ui.BouncerDialogFactory
+import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.dagger.SysUISingleton
@@ -39,12 +37,9 @@
data class ComposeBouncerDependencies
@Inject
constructor(
- val legacyInteractor: PrimaryBouncerInteractor,
val viewModelFactory: BouncerSceneContentViewModel.Factory,
val dialogFactory: BouncerDialogFactory,
- val authenticationInteractor: AuthenticationInteractor,
- val viewMediatorCallback: ViewMediatorCallback?,
- val selectedUserInteractor: SelectedUserInteractor,
+ val bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
)
/**
@@ -55,21 +50,17 @@
class BouncerViewBinder
@Inject
constructor(
- private val composeBouncerFlags: ComposeBouncerFlags,
private val legacyBouncerDependencies: Lazy<LegacyBouncerDependencies>,
private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>,
) {
fun bind(view: ViewGroup) {
- if (composeBouncerFlags.isOnlyComposeBouncerEnabled()) {
+ if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) {
val deps = composeBouncerDependencies.get()
ComposeBouncerViewBinder.bind(
view,
- deps.legacyInteractor,
deps.viewModelFactory,
deps.dialogFactory,
- deps.authenticationInteractor,
- deps.selectedUserInteractor,
- deps.viewMediatorCallback,
+ deps.bouncerContainerViewModelFactory,
)
} else {
val deps = legacyBouncerDependencies.get()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index 102ae7a..b5e54d5 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -5,100 +5,55 @@
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.compose.ui.platform.ComposeView
-import androidx.core.view.isVisible
+import androidx.core.view.isGone
import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.compose.theme.PlatformTheme
-import com.android.keyguard.ViewMediatorCallback
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.composable.BouncerContent
+import com.android.systemui.bouncer.ui.composable.BouncerContainer
+import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
-import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import kotlinx.coroutines.awaitCancellation
/** View binder responsible for binding the compose version of the bouncer. */
object ComposeBouncerViewBinder {
fun bind(
view: ViewGroup,
- legacyInteractor: PrimaryBouncerInteractor,
viewModelFactory: BouncerSceneContentViewModel.Factory,
dialogFactory: BouncerDialogFactory,
- authenticationInteractor: AuthenticationInteractor,
- selectedUserInteractor: SelectedUserInteractor,
- viewMediatorCallback: ViewMediatorCallback?,
+ bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
) {
- view.addView(
- ComposeView(view.context).apply {
- repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- setViewTreeOnBackPressedDispatcherOwner(
- object : OnBackPressedDispatcherOwner {
- override val onBackPressedDispatcher =
- OnBackPressedDispatcher().apply {
- setOnBackInvokedDispatcher(
- view.viewRootImpl.onBackInvokedDispatcher
- )
- }
-
- override val lifecycle: Lifecycle =
- [email protected]
- }
- )
- setContent {
- PlatformTheme {
- BouncerContent(
- rememberViewModel("ComposeBouncerViewBinder") {
- viewModelFactory.create()
- },
- dialogFactory,
- )
- }
- }
- }
- }
- }
- )
-
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch {
- legacyInteractor.isShowing.collectLatest { bouncerShowing ->
- view.isVisible = bouncerShowing
- }
- }
-
- launch {
- authenticationInteractor.onAuthenticationResult.collectLatest {
- authenticationSucceeded ->
- if (authenticationSucceeded) {
- // Some dismiss actions require that keyguard be dismissed right away or
- // deferred until something else later on dismisses keyguard (eg. end of
- // a hide animation).
- val deferKeyguardDone =
- legacyInteractor.bouncerDismissAction?.onDismissAction?.onDismiss()
- legacyInteractor.setDismissAction(null, null)
-
- viewMediatorCallback?.let {
- val selectedUserId = selectedUserInteractor.getSelectedUserId()
- if (deferKeyguardDone == true) {
- it.keyguardDonePending(selectedUserId)
- } else {
- it.keyguardDone(selectedUserId)
+ view.viewModel(
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = { bouncerContainerViewModelFactory.create() },
+ traceName = "ComposeBouncerViewBinder",
+ ) { viewModel ->
+ try {
+ view.setViewTreeOnBackPressedDispatcherOwner(
+ object : OnBackPressedDispatcherOwner {
+ override val onBackPressedDispatcher =
+ OnBackPressedDispatcher().apply {
+ setOnBackInvokedDispatcher(
+ view.viewRootImpl.onBackInvokedDispatcher
+ )
}
- }
+
+ override val lifecycle: Lifecycle = [email protected]
}
- }
- }
- launch {
- legacyInteractor.startingDisappearAnimation.collectLatest {
- it.run()
- legacyInteractor.hide()
- }
+ )
+
+ view.addView(
+ ComposeView(view.context).apply {
+ setContent { BouncerContainer(viewModelFactory, dialogFactory) }
+ }
+ )
+ view.setSnapshotBinding { view.isGone = !viewModel.isVisible }
+ awaitCancellation()
+ } finally {
+ view.removeAllViews()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
new file mode 100644
index 0000000..c05dcd5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.bouncer.ui.composable
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.bouncer.ui.BouncerDialogFactory
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.rememberViewModel
+
+/** Container that includes the compose bouncer and is meant to be included in legacy keyguard. */
+@Composable
+fun BouncerContainer(
+ viewModelFactory: BouncerSceneContentViewModel.Factory,
+ dialogFactory: BouncerDialogFactory,
+) {
+ PlatformTheme {
+ val backgroundColor = MaterialTheme.colorScheme.surface
+
+ val bouncerViewModel = rememberViewModel("BouncerContainer") { viewModelFactory.create() }
+ Box {
+ Canvas(Modifier.fillMaxSize()) { drawRect(color = backgroundColor) }
+
+ // Separate the bouncer content into a reusable composable that
+ // doesn't have any SceneScope
+ // dependencies
+ BouncerContent(
+ bouncerViewModel,
+ dialogFactory,
+ Modifier.sysuiResTag(Bouncer.TestTags.Root).fillMaxSize()
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticHelper.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticHelper.kt
new file mode 100644
index 0000000..1faacff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticHelper.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.bouncer.ui.helper
+
+import android.view.HapticFeedbackConstants
+import android.view.View
+import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags
+//noinspection CleanArchitectureDependencyViolation: Data layer only referenced for this enum class
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.MSDLPlayer
+
+/** A helper object to deliver haptic feedback in bouncer interactions. */
+object BouncerHapticHelper {
+
+ private val authInteractionProperties = AuthInteractionProperties()
+
+ /**
+ * Deliver MSDL feedback as a result of authenticating through a bouncer.
+ *
+ * @param[authenticationSucceeded] Whether the authentication was successful or not.
+ * @param[player] The [MSDLPlayer] that delivers the correct feedback.
+ */
+ fun playMSDLAuthenticationFeedback(
+ authenticationSucceeded: Boolean,
+ player: MSDLPlayer?,
+ ) {
+ if (player == null || !Flags.msdlFeedback()) {
+ return
+ }
+
+ val token =
+ if (authenticationSucceeded) {
+ MSDLToken.UNLOCK
+ } else {
+ MSDLToken.FAILURE
+ }
+ player.playToken(token, authInteractionProperties)
+ }
+
+ /**
+ * Deliver feedback when dragging through cells in the pattern bouncer. This function can play
+ * MSDL feedback using a [MSDLPlayer], or fallback to a default haptic feedback using the
+ * [View.performHapticFeedback] API and a [View].
+ *
+ * @param[player] [MSDLPlayer] for MSDL feedback.
+ * @param[view] A [View] for default haptic feedback using [View.performHapticFeedback]
+ */
+ fun playPatternDotFeedback(player: MSDLPlayer?, view: View?) {
+ if (player == null || !Flags.msdlFeedback()) {
+ view?.performHapticFeedback(
+ HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
+ )
+ } else {
+ player.playToken(MSDLToken.DRAG_INDICATOR)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
new file mode 100644
index 0000000..d223657
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.bouncer.ui.viewmodel
+
+import androidx.compose.runtime.getValue
+import com.android.keyguard.ViewMediatorCallback
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+class BouncerContainerViewModel
+@AssistedInject
+constructor(
+ private val legacyInteractor: PrimaryBouncerInteractor,
+ private val authenticationInteractor: AuthenticationInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val viewMediatorCallback: ViewMediatorCallback?,
+) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("BouncerContainerViewModel")
+
+ val isVisible: Boolean by
+ hydrator.hydratedStateOf(traceName = "isVisible", source = legacyInteractor.isShowing)
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch {
+ authenticationInteractor.onAuthenticationResult.collect { authenticationSucceeded ->
+ if (authenticationSucceeded) {
+ // Some dismiss actions require that keyguard be dismissed right away or
+ // deferred until something else later on dismisses keyguard (eg. end of
+ // a hide animation).
+ val deferKeyguardDone =
+ legacyInteractor.bouncerDismissAction?.onDismissAction?.onDismiss()
+ legacyInteractor.setDismissAction(null, null)
+
+ viewMediatorCallback?.let {
+ val selectedUserId = selectedUserInteractor.getSelectedUserId()
+ if (deferKeyguardDone == true) {
+ it.keyguardDonePending(selectedUserId)
+ } else {
+ it.keyguardDone(selectedUserId)
+ }
+ }
+ }
+ }
+ }
+
+ launch {
+ legacyInteractor.startingDisappearAnimation.collect {
+ it.run()
+ legacyInteractor.hide()
+ }
+ }
+
+ hydrator.activate()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): BouncerContainerViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
index e54dc7d..c383b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
@@ -78,7 +78,6 @@
private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
- private val flags: ComposeBouncerFlags,
) : ExclusiveActivatable() {
/**
* A message shown when the user has attempted the wrong credential too many times and now must
@@ -96,7 +95,7 @@
val message: MutableStateFlow<MessageViewModel?> = MutableStateFlow(null)
override suspend fun onActivated(): Nothing {
- if (!flags.isComposeBouncerOrSceneContainerEnabled()) {
+ if (!ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
return awaitCancellation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
index adc4bc9..0aada06 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -29,7 +29,6 @@
import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
@@ -57,7 +56,6 @@
private val authenticationInteractor: AuthenticationInteractor,
private val devicePolicyManager: DevicePolicyManager,
private val bouncerMessageViewModelFactory: BouncerMessageViewModel.Factory,
- private val flags: ComposeBouncerFlags,
private val userSwitcher: UserSwitcherViewModel,
private val actionButtonInteractor: BouncerActionButtonInteractor,
private val pinViewModelFactory: PinBouncerViewModel.Factory,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
index 2d57e5b..4fe6fc6 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
@@ -22,7 +22,7 @@
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.map
@@ -31,11 +31,11 @@
* Models UI state for user actions that can lead to navigation to other scenes when showing the
* bouncer scene.
*/
-class BouncerSceneActionsViewModel
+class BouncerUserActionsViewModel
@AssistedInject
constructor(
private val bouncerInteractor: BouncerInteractor,
-) : SceneActionsViewModel() {
+) : UserActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
bouncerInteractor.dismissDestination
@@ -50,6 +50,6 @@
@AssistedFactory
interface Factory {
- fun create(): BouncerSceneActionsViewModel
+ fun create(): BouncerUserActionsViewModel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index d2caefd..83d4091 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -34,8 +34,6 @@
import com.android.systemui.classifier.FalsingDataProvider.SessionListener;
import com.android.systemui.classifier.HistoryTracker.BeliefListener;
import com.android.systemui.dagger.qualifiers.TestHarness;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -76,7 +74,6 @@
private final boolean mTestHarness;
private final MetricsLogger mMetricsLogger;
private int mIsFalseTouchCalls;
- private FeatureFlags mFeatureFlags;
private static final Queue<String> RECENT_INFO_LOG =
new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1);
private static final Queue<DebugSwipeRecord> RECENT_SWIPES =
@@ -186,8 +183,7 @@
DoubleTapClassifier doubleTapClassifier, HistoryTracker historyTracker,
KeyguardStateController keyguardStateController,
AccessibilityManager accessibilityManager,
- @TestHarness boolean testHarness,
- FeatureFlags featureFlags) {
+ @TestHarness boolean testHarness) {
mDataProvider = falsingDataProvider;
mMetricsLogger = metricsLogger;
mClassifiers = classifiers;
@@ -198,7 +194,6 @@
mKeyguardStateController = keyguardStateController;
mAccessibilityManager = accessibilityManager;
mTestHarness = testHarness;
- mFeatureFlags = featureFlags;
mDataProvider.addSessionListener(mSessionListener);
mDataProvider.addGestureCompleteListener(mGestureFinalizedListener);
@@ -399,8 +394,8 @@
|| mDataProvider.isA11yAction()
|| mDataProvider.isFromTrackpad()
|| mDataProvider.isFromKeyboard()
- || (mFeatureFlags.isEnabled(Flags.FALSING_OFF_FOR_UNFOLDED)
- && mDataProvider.isUnfolded());
+ || !mDataProvider.isTouchScreenSource()
+ || mDataProvider.isUnfolded();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 2eca02c..962ab99 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -21,6 +21,7 @@
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricSourceType;
import android.util.Log;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -28,6 +29,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Flags;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -343,7 +345,9 @@
// will be ignored by the collector until another MotionEvent.ACTION_DOWN is passed in.
// avoidGesture must be called immediately following the MotionEvent.ACTION_DOWN, before
// any other events are processed, otherwise the whole gesture will be recorded.
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ //
+ // We should only delay processing of these events for touchscreen sources
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN && isTouchscreenSource(ev)) {
// Make a copy of ev, since it will be recycled after we exit this method.
mPendingDownEvent = MotionEvent.obtain(ev);
mAvoidGesture = false;
@@ -410,6 +414,22 @@
mFalsingDataProvider.onA11yAction();
}
+ /**
+ * returns {@code true} if the device supports Touchscreen, {@code false} otherwise. Defaults to
+ * {@code true} if the device is {@code null}
+ */
+ private boolean isTouchscreenSource(MotionEvent ev) {
+ if (!Flags.nonTouchscreenDevicesBypassFalsing()) {
+ return true;
+ }
+ InputDevice device = ev.getDevice();
+ if (device != null) {
+ return device.supportsSource(InputDevice.SOURCE_TOUCHSCREEN);
+ } else {
+ return true;
+ }
+ }
+
private boolean shouldSessionBeActive() {
return mScreenOn
&& (mState == StatusBarState.KEYGUARD)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index 1501701..769976e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -20,11 +20,13 @@
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.util.DisplayMetrics;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -281,6 +283,9 @@
}
public boolean isFromTrackpad() {
+ if (Flags.nonTouchscreenDevicesBypassFalsing()) {
+ return false;
+ }
if (mRecentMotionEvents.isEmpty()) {
return false;
}
@@ -290,6 +295,25 @@
|| classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
}
+ /**
+ * returns {@code true} if the device supports Touchscreen, {@code false} otherwise. Defaults to
+ * {@code true} if the device is {@code null}
+ */
+ public boolean isTouchScreenSource() {
+ if (!Flags.nonTouchscreenDevicesBypassFalsing()) {
+ return true;
+ }
+ if (mRecentMotionEvents.isEmpty()) {
+ return true;
+ }
+ InputDevice device = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1).getDevice();
+ if (device != null) {
+ return device.supportsSource(InputDevice.SOURCE_TOUCHSCREEN);
+ } else {
+ return true;
+ }
+ }
+
private void recalculateData() {
if (!mDirty) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
index 0dc6fda..dc3b50c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
@@ -27,6 +27,7 @@
import android.view.WindowInsets;
import android.view.WindowManager;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
import com.android.systemui.screenshot.FloatingWindowUtil;
@@ -44,6 +45,7 @@
private final Context mContext;
private final WindowManager mWindowManager;
+ private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private boolean mKeyboardVisible;
@@ -52,7 +54,9 @@
private Runnable mOnOrientationChangeListener;
@Inject
- ClipboardOverlayWindow(@OverlayWindowContext Context context) {
+ ClipboardOverlayWindow(@OverlayWindowContext Context context,
+ @OverlayWindowContext ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ @OverlayWindowContext WindowManager windowManager) {
super(context);
mContext = context;
mOrientation = mContext.getResources().getConfiguration().orientation;
@@ -61,10 +65,11 @@
requestFeature(Window.FEATURE_NO_TITLE);
requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
setBackgroundDrawableResource(android.R.color.transparent);
- mWindowManager = mContext.getSystemService(WindowManager.class);
+ mWindowManager = windowManager;
+ mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
mWindowLayoutParams.setTitle("ClipboardOverlay");
- setWindowManager(mWindowManager, null, null);
+ setWindowManager(windowManager, null, null);
setWindowFocusable(false);
}
@@ -81,10 +86,12 @@
attach();
withWindowAttached(() -> {
- WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+ WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics()
+ .getWindowInsets();
mKeyboardVisible = currentInsets.isVisible(WindowInsets.Type.ime());
peekDecorView().getViewTreeObserver().addOnGlobalLayoutListener(() -> {
- WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+ WindowInsets insets = mWindowManager.getCurrentWindowMetrics()
+ .getWindowInsets();
boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime());
if (keyboardVisible != mKeyboardVisible) {
mKeyboardVisible = keyboardVisible;
@@ -105,7 +112,7 @@
void remove() {
final View decorView = peekDecorView();
if (decorView != null && decorView.isAttachedToWindow()) {
- mWindowManager.removeViewImmediate(decorView);
+ mViewCaptureAwareWindowManager.removeViewImmediate(decorView);
}
}
@@ -139,7 +146,7 @@
if (decorView.isAttachedToWindow()) {
return;
}
- mWindowManager.addView(decorView, mWindowLayoutParams);
+ mViewCaptureAwareWindowManager.addView(decorView, mWindowLayoutParams);
decorView.requestApplyInsets();
}
@@ -160,7 +167,7 @@
}
final View decorView = peekDecorView();
if (decorView != null && decorView.isAttachedToWindow()) {
- mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
+ mViewCaptureAwareWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
index ff9fba4..307a07f 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -18,17 +18,24 @@
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
+import static com.android.systemui.Flags.enableViewCaptureTracing;
+import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.view.Display;
import android.view.LayoutInflater;
+import android.view.WindowManager;
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -61,6 +68,28 @@
R.layout.clipboard_overlay, null);
}
+ /**
+ *
+ */
+ @Provides
+ @OverlayWindowContext
+ static WindowManager provideWindowManager(@OverlayWindowContext Context context) {
+ return context.getSystemService(WindowManager.class);
+ }
+
+ /**
+ *
+ */
+ @Provides
+ @OverlayWindowContext
+ static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
+ @OverlayWindowContext WindowManager windowManager,
+ Lazy<ViewCapture> daggerLazyViewCapture) {
+ return new ViewCaptureAwareWindowManager(windowManager,
+ /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
+ /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+ }
+
@Qualifier
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 3cdb573..aef5f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -38,5 +38,5 @@
}
/** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */
-fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon =
+fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon.Loaded =
Icon.Loaded(this, contentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
index 578389b..13f6bba 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
@@ -23,35 +23,25 @@
import androidx.annotation.DimenRes
import androidx.annotation.LayoutRes
import com.android.settingslib.Utils
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
import com.android.systemui.statusbar.policy.onThemeChanged
import com.android.systemui.util.kotlin.emitOnStart
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-/** Configuration-aware-state-tracking utilities. */
-class ConfigurationState
-@Inject
-constructor(
- private val configurationController: ConfigurationController,
- @Application private val context: Context,
- private val layoutInflater: LayoutInflater,
-) {
+interface ConfigurationState {
/**
* Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
* configuration.
*
* @see android.content.res.Resources.getDimensionPixelSize
*/
- fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int> {
- return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
- context.resources.getDimensionPixelSize(id)
- }
- }
+ fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int>
/**
* Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
@@ -59,22 +49,14 @@
*
* @see android.content.res.Resources.getDimensionPixelSize
*/
- fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int> {
- return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
- context.resources.getDimensionPixelOffset(id)
- }
- }
+ fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int>
/**
* Returns a [Flow] that emits a color that is kept in sync with the device theme.
*
* @see Utils.getColorAttrDefaultColor
*/
- fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int> {
- return configurationController.onThemeChanged.emitOnStart().map {
- Utils.getColorAttrDefaultColor(context, id, defaultValue)
- }
- }
+ fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int>
/**
* Returns a [Flow] that emits a [View] that is re-inflated as necessary to remain in sync with
@@ -87,6 +69,65 @@
@LayoutRes id: Int,
root: ViewGroup?,
attachToRoot: Boolean,
+ ): Flow<T>
+}
+
+/** Configuration-aware-state-tracking utilities. */
+class ConfigurationStateImpl
+@AssistedInject
+constructor(
+ @Assisted private val configurationController: ConfigurationController,
+ @Assisted private val context: Context,
+) : ConfigurationState {
+
+ private val layoutInflater = LayoutInflater.from(context)
+
+ /**
+ * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
+ * configuration.
+ *
+ * @see android.content.res.Resources.getDimensionPixelSize
+ */
+ override fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int> {
+ return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
+ context.resources.getDimensionPixelSize(id)
+ }
+ }
+
+ /**
+ * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
+ * configuration.
+ *
+ * @see android.content.res.Resources.getDimensionPixelSize
+ */
+ override fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int> {
+ return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
+ context.resources.getDimensionPixelOffset(id)
+ }
+ }
+
+ /**
+ * Returns a [Flow] that emits a color that is kept in sync with the device theme.
+ *
+ * @see Utils.getColorAttrDefaultColor
+ */
+ override fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int> {
+ return configurationController.onThemeChanged.emitOnStart().map {
+ Utils.getColorAttrDefaultColor(context, id, defaultValue)
+ }
+ }
+
+ /**
+ * Returns a [Flow] that emits a [View] that is re-inflated as necessary to remain in sync with
+ * the device configuration.
+ *
+ * @see LayoutInflater.inflate
+ */
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : View> inflateLayout(
+ @LayoutRes id: Int,
+ root: ViewGroup?,
+ attachToRoot: Boolean,
): Flow<T> {
// TODO(b/305930747): This may lead to duplicate invocations if both flows emit, find a
// solution to only emit one event.
@@ -97,4 +138,16 @@
.emitOnStart()
.map { layoutInflater.inflate(id, root, attachToRoot) as T }
}
+
+ @AssistedFactory
+ interface Factory {
+ /**
+ * Creates a configurationState for a given context. The [configurationController] is
+ * supposed to give config events specific for that context.
+ */
+ fun create(
+ context: Context,
+ configurationController: ConfigurationController
+ ): ConfigurationStateImpl
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt
new file mode 100644
index 0000000..b36da3b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.common.ui
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
+
+/**
+ * Annotates elements that provide information from the global configuration.
+ *
+ * The global configuration is the one associted with the main display. Secondary displays will
+ * apply override to the global configuration. Elements annotated with this shouldn't be used for
+ * secondary displays.
+ */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class GlobalConfig
+
+@Module
+interface ConfigurationStateModule {
+
+ /**
+ * Deprecated: [ConfigurationState] should be injected only with the correct annotation. For
+ * now, without annotation the global config associated state is provided.
+ */
+ @Binds
+ fun provideGlobalConfigurationState(
+ @GlobalConfig configurationState: ConfigurationState
+ ): ConfigurationState
+
+ companion object {
+ @SysUISingleton
+ @Provides
+ @GlobalConfig
+ fun provideGlobalConfigurationState(
+ configStateFactory: ConfigurationStateImpl.Factory,
+ configurationController: ConfigurationController,
+ @Application context: Context,
+ ): ConfigurationState {
+ return configStateFactory.create(context, configurationController)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
index b6ace81..9c4736a 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
@@ -27,6 +27,7 @@
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.shade.TouchLogger
import kotlin.math.pow
import kotlin.math.sqrt
@@ -42,6 +43,8 @@
context: Context,
attrs: AttributeSet?,
longPressDuration: () -> Long,
+ allowedTouchSlop: Int = ViewConfiguration.getTouchSlop(),
+ logger: LongPressHandlingViewLogger? = null,
) :
View(
context,
@@ -97,6 +100,8 @@
},
onSingleTapDetected = { listener?.onSingleTapDetected(this@LongPressHandlingView) },
longPressDuration = longPressDuration,
+ allowedTouchSlop = allowedTouchSlop,
+ logger = logger,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
index d3fc610..4e38a49 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
@@ -17,7 +17,7 @@
package com.android.systemui.common.ui.view
-import android.view.ViewConfiguration
+import com.android.systemui.log.LongPressHandlingViewLogger
import kotlinx.coroutines.DisposableHandle
/** Encapsulates logic to handle complex touch interactions with a [LongPressHandlingView]. */
@@ -35,6 +35,14 @@
private val onSingleTapDetected: () -> Unit,
/** Time for the touch to be considered a long-press in ms */
var longPressDuration: () -> Long,
+ /**
+ * Default touch slop that is allowed, if the movement between [MotionEventModel.Down] and
+ * [MotionEventModel.Up] is more than [allowedTouchSlop] then the touch is not processed as
+ * single tap or a long press.
+ */
+ val allowedTouchSlop: Int,
+ /** Optional logger that can be passed in to log touch events */
+ val logger: LongPressHandlingViewLogger? = null,
) {
sealed class MotionEventModel {
object Other : MotionEventModel()
@@ -70,22 +78,26 @@
true
}
is MotionEventModel.Move -> {
- if (event.distanceMoved > ViewConfiguration.getTouchSlop()) {
+ if (event.distanceMoved > allowedTouchSlop) {
+ logger?.cancelingLongPressDueToTouchSlop(event.distanceMoved, allowedTouchSlop)
cancelScheduledLongPress()
}
false
}
is MotionEventModel.Up -> {
+ logger?.onUpEvent(event.distanceMoved, allowedTouchSlop, event.gestureDuration)
cancelScheduledLongPress()
if (
- event.distanceMoved <= ViewConfiguration.getTouchSlop() &&
+ event.distanceMoved <= allowedTouchSlop &&
event.gestureDuration < longPressDuration()
) {
+ logger?.dispatchingSingleTap()
dispatchSingleTap()
}
false
}
is MotionEventModel.Cancel -> {
+ logger?.motionEventCancelled()
cancelScheduledLongPress()
false
}
@@ -97,15 +109,18 @@
x: Int,
y: Int,
) {
+ val duration = longPressDuration()
+ logger?.schedulingLongPress(duration)
scheduledLongPressHandle =
postDelayed(
{
+ logger?.longPressTriggered()
dispatchLongPress(
x = x,
y = y,
)
},
- longPressDuration(),
+ duration,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index c69cea4..04393fe 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -21,6 +21,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
import com.android.systemui.Flags.restartDreamOnUnocclude
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -55,6 +56,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val dreamManager: DreamManager,
+ private val communalSceneInteractor: CommunalSceneInteractor,
@Background private val bgScope: CoroutineScope,
) : CoreStartable {
/** Flow that emits when the dream should be started underneath the glanceable hub. */
@@ -66,6 +68,8 @@
not(keyguardInteractor.isDreaming),
// TODO(b/362830856): Remove this workaround.
keyguardInteractor.isKeyguardShowing,
+ not(communalSceneInteractor.isLaunchingWidget),
+ not(keyguardInteractor.isKeyguardOccluded),
)
.filter { it }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 9b96341..b570e14 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -61,6 +61,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.ManagedProfileController
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.emitOnStart
@@ -116,6 +117,7 @@
sceneInteractor: SceneInteractor,
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
+ private val managedProfileController: ManagedProfileController
) {
private val logger = Logger(logBuffer, "CommunalInteractor")
@@ -401,12 +403,7 @@
/** Request to unpause work profile that is currently in quiet mode. */
fun unpauseWorkProfile() {
- userTracker.userProfiles
- .find { it.isManagedProfile }
- ?.userHandle
- ?.let { userHandle ->
- userManager.requestQuietModeEnabled(/* enableQuietMode */ false, userHandle)
- }
+ managedProfileController.setWorkModeEnabled(true)
}
/** Returns true if work profile is in quiet mode (disabled) for user handle. */
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 8f756a2..ac496f0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -195,7 +195,7 @@
is ObservableTransitionState.Idle ->
flowOf(CommunalTransitionProgressModel.Idle(state.currentScene))
is ObservableTransitionState.Transition ->
- if (state.toScene == targetScene) {
+ if (state.toContent == targetScene) {
state.progress.map {
CommunalTransitionProgressModel.Transition(
// Clamp the progress values between 0 and 1 as actual progress
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index e04d309..c7538bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -161,7 +161,7 @@
if (
prevTransition is ObservableTransitionState.Transition &&
currentTransitionId != null &&
- idle.currentScene == prevTransition.toScene
+ idle.currentScene == prevTransition.toContent
) {
finishCurrentTransition()
} else {
@@ -219,17 +219,19 @@
prevTransition: ObservableTransitionState,
transition: ObservableTransitionState.Transition
) {
- if (prevTransition.isTransitioning(from = transition.fromScene, to = transition.toScene)) {
+ if (
+ prevTransition.isTransitioning(from = transition.fromContent, to = transition.toContent)
+ ) {
// This is a new transition, but exactly the same as the previous state. Skip resetting
// KTF for this case and just collect the new progress instead.
collectProgress(transition)
- } else if (transition.toScene == CommunalScenes.Communal) {
+ } else if (transition.toContent == CommunalScenes.Communal) {
if (currentToState == KeyguardState.GLANCEABLE_HUB) {
transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
}
startTransitionToGlanceableHub()
collectProgress(transition)
- } else if (transition.toScene == CommunalScenes.Blank) {
+ } else if (transition.toContent == CommunalScenes.Blank) {
// Another transition started before this one is completed. Transition to the
// GLANCEABLE_HUB state so that we can properly transition away from it.
transitionKtfTo(KeyguardState.GLANCEABLE_HUB)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
index 2352841f..1def5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
@@ -126,13 +126,13 @@
/** Whether currently transitioning from another scene to communal. */
private fun ObservableTransitionState.isSwipingToCommunal(): Boolean {
return this is ObservableTransitionState.Transition &&
- toScene == CommunalScenes.Communal &&
+ toContent == CommunalScenes.Communal &&
isInitiatedByUserInput
}
/** Whether currently transitioning from communal to another scene. */
private fun ObservableTransitionState.isSwipingFromCommunal(): Boolean {
return this is ObservableTransitionState.Transition &&
- fromScene == CommunalScenes.Communal &&
+ fromContent == CommunalScenes.Communal &&
isInitiatedByUserInput
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
index aed9215..83f31e5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
@@ -74,8 +74,8 @@
tag = TAG,
level = LogLevel.INFO,
messageInitializer = {
- str1 = transitionState.fromScene.toString()
- str2 = transitionState.toScene.toString()
+ str1 = transitionState.fromContent.toString()
+ str2 = transitionState.toContent.toString()
},
messagePrinter = { "Scene transition started: $str1 → $str2" },
)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index db7ffc1..037b6fa 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -48,6 +48,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.DeviceControlsTile
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -86,15 +87,16 @@
@IntoMap
@StringKey(DEVICE_CONTROLS_SPEC)
fun provideDeviceControlsTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
- QSTileConfig(
- tileSpec = TileSpec.create(DEVICE_CONTROLS_SPEC),
- uiConfig =
- QSTileUIConfig.Resource(
- iconRes = com.android.systemui.res.R.drawable.controls_icon,
- labelRes = com.android.systemui.res.R.string.quick_controls_title
- ),
- instanceId = uiEventLogger.getNewInstanceId(),
- )
+ QSTileConfig(
+ tileSpec = TileSpec.create(DEVICE_CONTROLS_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = com.android.systemui.res.R.drawable.controls_icon,
+ labelRes = com.android.systemui.res.R.string.quick_controls_title
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
+ )
}
@Binds
@@ -115,12 +117,12 @@
@Binds
abstract fun provideSettingsManager(
- manager: ControlsSettingsRepositoryImpl
+ manager: ControlsSettingsRepositoryImpl
): ControlsSettingsRepository
@Binds
abstract fun provideDialogManager(
- manager: ControlsSettingsDialogManagerImpl
+ manager: ControlsSettingsDialogManagerImpl
): ControlsSettingsDialogManager
@Binds
@@ -141,8 +143,7 @@
repository: SelectedComponentRepositoryImpl
): SelectedComponentRepository
- @BindsOptionalOf
- abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper
+ @BindsOptionalOf abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper
@BindsOptionalOf
abstract fun provideControlsTileResourceConfiguration(): ControlsTileResourceConfiguration
@@ -157,23 +158,17 @@
@Binds
@IntoMap
@ClassKey(ControlsFavoritingActivity::class)
- abstract fun provideControlsFavoritingActivity(
- activity: ControlsFavoritingActivity
- ): Activity
+ abstract fun provideControlsFavoritingActivity(activity: ControlsFavoritingActivity): Activity
@Binds
@IntoMap
@ClassKey(ControlsEditingActivity::class)
- abstract fun provideControlsEditingActivity(
- activity: ControlsEditingActivity
- ): Activity
+ abstract fun provideControlsEditingActivity(activity: ControlsEditingActivity): Activity
@Binds
@IntoMap
@ClassKey(ControlsRequestDialog::class)
- abstract fun provideControlsRequestDialog(
- activity: ControlsRequestDialog
- ): Activity
+ abstract fun provideControlsRequestDialog(activity: ControlsRequestDialog): Activity
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 21a704d..8818c3a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -202,6 +202,13 @@
return context.getSystemService(CaptioningManager.class);
}
+ @Provides
+ @Singleton
+ static UserScopedService<CaptioningManager> provideUserScopedCaptioningManager(
+ Context context) {
+ return new UserScopedServiceImpl<>(context, CaptioningManager.class);
+ }
+
/** */
@Provides
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 1dd3722..3fe6669 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -21,6 +21,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactoryBase;
+import com.android.systemui.common.ui.GlobalConfig;
import com.android.systemui.dagger.qualifiers.PerUser;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -127,6 +128,7 @@
* Creates a ContextComponentHelper.
*/
@SysUISingleton
+ @GlobalConfig
ConfigurationController getConfigurationController();
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 411cbd5..b55108d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,6 +48,7 @@
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
import com.android.systemui.common.data.CommonDataLayerModule;
+import com.android.systemui.common.ui.ConfigurationStateModule;
import com.android.systemui.common.usagestats.data.CommonUsageStatsDataLayerModule;
import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.complication.dagger.ComplicationComponent;
@@ -207,6 +208,7 @@
ClockRegistryModule.class,
CommunalModule.class,
CommonDataLayerModule.class,
+ ConfigurationStateModule.class,
CommonUsageStatsDataLayerModule.class,
ConfigurationControllerModule.class,
ConnectivityModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index d288cce..37c6e17 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -391,8 +391,10 @@
),
Pair(
if (SceneContainerFlag.isEnabled) {
- keyguardTransitionInteractor
- .isInTransitionWhere(toStatePredicate = { it == KeyguardState.UNDEFINED })
+ sceneInteractor
+ .get()
+ .transitionState
+ .map { it.isTransitioning(to = Scenes.Gone) || it.isIdle(Scenes.Gone) }
.isFalse()
} else {
keyguardRepository.isKeyguardGoingAway.isFalse()
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
index dff391a..cdd2b05 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -60,14 +60,23 @@
fun unregisterListener(listener: FaceAuthenticationListener)
fun onUdfpsSensorTouched()
+
fun onAssistantTriggeredOnLockScreen()
+
fun onDeviceLifted()
- fun onQsExpansionStared()
+
+ fun onShadeExpansionStarted()
+
fun onNotificationPanelClicked()
+
fun onSwipeUpOnBouncer()
+
fun onPrimaryBouncerUserInput()
+
fun onAccessibilityAction()
+
fun onWalletLaunched()
+
fun onDeviceUnfolded()
/** Whether face auth is considered class 3 */
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 39f4e31..7018f9dc 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -16,12 +16,14 @@
package com.android.systemui.deviceentry.domain.interactor
+import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.utils.coroutines.flow.mapLatestConflated
@@ -56,6 +58,7 @@
private val sceneInteractor: SceneInteractor,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
) {
/**
* Whether the device is unlocked.
@@ -126,17 +129,14 @@
},
isLockscreenEnabled,
deviceUnlockedInteractor.deviceUnlockStatus,
- isDeviceEntered) {
- isNoneAuthMethod,
- isLockscreenEnabled,
- deviceUnlockStatus,
- isDeviceEntered ->
- val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled
- (isSwipeAuthMethod ||
- (deviceUnlockStatus.isUnlocked &&
- deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
- !isDeviceEntered
- }
+ isDeviceEntered
+ ) { isNoneAuthMethod, isLockscreenEnabled, deviceUnlockStatus, isDeviceEntered ->
+ val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled
+ (isSwipeAuthMethod ||
+ (deviceUnlockStatus.isUnlocked &&
+ deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
+ !isDeviceEntered
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -150,8 +150,16 @@
/**
* Attempt to enter the device and dismiss the lockscreen. If authentication is required to
* unlock the device it will transition to bouncer.
+ *
+ * @param callback An optional callback to invoke when the attempt succeeds, fails, or is
+ * canceled
*/
- fun attemptDeviceEntry() {
+ @JvmOverloads
+ fun attemptDeviceEntry(
+ callback: IKeyguardDismissCallback? = null,
+ ) {
+ callback?.let { dismissCallbackRegistry.addCallback(it) }
+
// TODO (b/307768356),
// 1. Check if the device is already authenticated by trust agent/passive biometrics
// 2. Show SPFS/UDFPS bouncer if it is available AlternateBouncerInteractor.show
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
index de5d0aa..9b8c2b1 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
@@ -47,6 +47,7 @@
override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
override fun isFaceAuthStrong(): Boolean = false
+
override fun start() = Unit
override fun registerListener(listener: FaceAuthenticationListener) {}
@@ -59,13 +60,17 @@
override fun onDeviceLifted() {}
- override fun onQsExpansionStared() {}
+ override fun onShadeExpansionStarted() {}
override fun onNotificationPanelClicked() {}
override fun onSwipeUpOnBouncer() {}
+
override fun onPrimaryBouncerUserInput() {}
+
override fun onAccessibilityAction() {}
+
override fun onWalletLaunched() = Unit
+
override fun onDeviceUnfolded() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index 183e0e9..3b5d5a8 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -60,6 +60,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
@@ -214,6 +215,16 @@
}
}
.launchIn(applicationScope)
+
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor
+ .get()
+ .transitionState
+ .filter { it.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Shade) }
+ .distinctUntilChanged()
+ .onEach { onShadeExpansionStarted() }
+ .launchIn(applicationScope)
+ }
}
private val isBouncerVisible: Flow<Boolean> by lazy {
@@ -239,8 +250,8 @@
runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true)
}
- override fun onQsExpansionStared() {
- runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)
+ override fun onShadeExpansionStarted() {
+ runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, false)
}
override fun onDeviceLifted() {
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 40e2f17..1f5878b 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -48,6 +48,7 @@
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.shareIn
@@ -184,6 +185,9 @@
if (Flags.enableEfficientDisplayRepository()) {
enabledDisplayIds
.mapElementsLazily { displayId -> getDisplay(displayId) }
+ .onEach {
+ if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.")
+ }
.flowOn(backgroundCoroutineDispatcher)
.debugLog("enabledDisplays")
.stateIn(
@@ -194,7 +198,8 @@
// performance concerns.
// Ultimately, this is a trade-off between a one-time UI thread binder call and
// the constant overhead of sharedFlows.
- initialValue = getDisplays())
+ initialValue = getDisplays()
+ )
} else {
oldEnabledDisplays
}
@@ -380,9 +385,8 @@
val resultSet: Set<V>
)
- val initialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
-
- return this.scan(initialState) { state, currentSet ->
+ val emptyInitialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
+ return this.scan(emptyInitialState) { state, currentSet ->
if (currentSet == state.previousSet) {
state
} else {
@@ -397,6 +401,7 @@
State(currentSet, newMap, resultSet)
}
}
+ .filter { it != emptyInitialState }
.map { it.resultSet }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index e3f740e..113e0011 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -185,6 +185,7 @@
mShadeExpanded = expanded;
updateLifecycleStateLocked();
+ updateGestureBlockingLocked();
});
}
};
@@ -215,6 +216,7 @@
mBouncerShowing = bouncerShowing;
updateLifecycleStateLocked();
+ updateGestureBlockingLocked();
});
}
};
@@ -248,7 +250,7 @@
* others.
*/
public void reset(String source) {
- reset(()-> {}, source);
+ reset(() -> {}, source);
}
/**
@@ -473,7 +475,7 @@
mLifecycleOwner,
new HashSet<>(Arrays.asList(
dreamComplicationComponent.getHideComplicationTouchHandler(),
- dreamOverlayComponent.getCommunalTouchHandler())));
+ dreamOverlayComponent.getCommunalTouchHandler())), TAG);
setLifecycleStateLocked(Lifecycle.State.STARTED);
@@ -525,11 +527,7 @@
mStarted = true;
updateRedirectWakeup();
-
- if (!isDreamInPreviewMode()) {
- mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER,
- GestureInteractor.Scope.Global);
- }
+ updateGestureBlockingLocked();
}
private void updateRedirectWakeup() {
@@ -553,6 +551,19 @@
null);
}
+ private void updateGestureBlockingLocked() {
+ final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing
+ && !isDreamInPreviewMode();
+
+ if (shouldBlock) {
+ mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER,
+ GestureInteractor.Scope.Global);
+ } else {
+ mGestureInteractor.removeGestureBlockedMatcher(DREAM_TYPE_MATCHER,
+ GestureInteractor.Scope.Global);
+ }
+ }
+
private Lifecycle.State getLifecycleStateLocked() {
return mLifecycleRegistry.getCurrentState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index f6ac7a5..a45ad15 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -38,6 +38,7 @@
import com.android.systemui.dreams.homecontrols.HomeControlsDreamService;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.pipeline.shared.TileSpec;
+import com.android.systemui.qs.shared.model.TileCategory;
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy;
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
@@ -196,6 +197,7 @@
R.drawable.ic_qs_screen_saver,
R.string.quick_settings_screensaver_label),
uiEventLogger.getNewInstanceId(),
+ TileCategory.UTILITIES,
tileSpec.getSpec(),
QSTilePolicy.NoRestrictions.INSTANCE
);
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
index 87eeebf..43855d9 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
@@ -19,13 +19,12 @@
import android.hardware.input.InputManager
import android.hardware.input.InputManager.KeyGestureEventListener
import android.hardware.input.KeyGestureEvent
+import android.os.SystemProperties
import com.android.systemui.CoreStartable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.contextualeducation.GestureType.BACK
-import com.android.systemui.contextualeducation.GestureType.HOME
-import com.android.systemui.contextualeducation.GestureType.OVERVIEW
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.education.dagger.ContextualEducationModule.EduClock
@@ -37,7 +36,10 @@
import java.time.Clock
import java.util.concurrent.Executor
import javax.inject.Inject
-import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.days
+import kotlin.time.DurationUnit
+import kotlin.time.toDuration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -60,7 +62,21 @@
companion object {
const val TAG = "KeyboardTouchpadEduInteractor"
const val MAX_SIGNAL_COUNT: Int = 2
- val usageSessionDuration = 72.hours
+ const val MAX_EDUCATION_SHOW_COUNT: Int = 2
+ val usageSessionDuration =
+ getDurationForConfig("persist.contextual_edu.usage_session_sec", 3.days)
+ val minIntervalBetweenEdu =
+ getDurationForConfig("persist.contextual_edu.edu_interval_sec", 7.days)
+
+ private fun getDurationForConfig(
+ systemPropertyKey: String,
+ defaultDuration: Duration
+ ): Duration =
+ SystemProperties.getLong(
+ systemPropertyKey,
+ /* defaultValue= */ defaultDuration.inWholeSeconds
+ )
+ .toDuration(DurationUnit.SECONDS)
}
private val _educationTriggered = MutableStateFlow<EducationInfo?>(null)
@@ -68,11 +84,9 @@
private val keyboardShortcutTriggered: Flow<GestureType> = conflatedCallbackFlow {
val listener = KeyGestureEventListener { event ->
+ // Only store keyboard shortcut time for gestures providing keyboard education
val shortcutType =
when (event.keyGestureType) {
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK -> BACK
- KeyGestureEvent.KEY_GESTURE_TYPE_HOME -> HOME
- KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS -> OVERVIEW
KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS -> ALL_APPS
else -> null
}
@@ -132,10 +146,20 @@
}
private fun isEducationNeeded(model: GestureEduModel): Boolean {
- // Todo: b/354884305 - add complete education logic to show education in correct scenarios
+ val lessThanMaxEduCount = model.educationShownCount < MAX_EDUCATION_SHOW_COUNT
val noShortcutTriggered = model.lastShortcutTriggeredTime == null
val signalCountReached = model.signalCount >= MAX_SIGNAL_COUNT
- return noShortcutTriggered && signalCountReached
+ val isPreviousEduOlderThanMinInterval =
+ if (model.educationShownCount == 1) {
+ model.lastEducationTime
+ ?.plusSeconds(minIntervalBetweenEdu.inWholeSeconds)
+ ?.isBefore(clock.instant()) ?: true
+ } else true
+
+ return lessThanMaxEduCount &&
+ noShortcutTriggered &&
+ signalCountReached &&
+ isPreviousEduOlderThanMinInterval
}
private fun isUsageSessionExpired(model: GestureEduModel): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
index 3223433..0e2d9b6 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
@@ -16,11 +16,25 @@
package com.android.systemui.education.domain.interactor
+import android.os.SystemProperties
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.education.dagger.ContextualEducationModule.EduClock
+import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
+import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
+import java.time.Clock
import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.hours
+import kotlin.time.DurationUnit
+import kotlin.time.toDuration
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
/**
@@ -39,12 +53,29 @@
@Inject
constructor(
@Background private val backgroundScope: CoroutineScope,
- private val contextualEducationInteractor: ContextualEducationInteractor
+ private val contextualEducationInteractor: ContextualEducationInteractor,
+ private val inputDeviceRepository: UserInputDeviceRepository,
+ private val tutorialRepository: TutorialSchedulerRepository,
+ @EduClock private val clock: Clock,
) : KeyboardTouchpadEduStatsInteractor {
+ companion object {
+ val initialDelayDuration: Duration
+ get() =
+ SystemProperties.getLong(
+ "persist.contextual_edu.initial_delay_sec",
+ /* defaultValue= */ 72.hours.inWholeSeconds
+ )
+ .toDuration(DurationUnit.SECONDS)
+ }
+
override fun incrementSignalCount(gestureType: GestureType) {
- // Todo: check if keyboard/touchpad is connected before update
- backgroundScope.launch { contextualEducationInteractor.incrementSignalCount(gestureType) }
+ backgroundScope.launch {
+ val targetDevice = getTargetDevice(gestureType)
+ if (isTargetDeviceConnected(targetDevice) && hasInitialDelayElapsed(targetDevice)) {
+ contextualEducationInteractor.incrementSignalCount(gestureType)
+ }
+ }
}
override fun updateShortcutTriggerTime(gestureType: GestureType) {
@@ -52,4 +83,29 @@
contextualEducationInteractor.updateShortcutTriggerTime(gestureType)
}
}
+
+ private suspend fun isTargetDeviceConnected(deviceType: DeviceType): Boolean {
+ return when (deviceType) {
+ KEYBOARD -> inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected
+ TOUCHPAD -> inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected
+ }
+ }
+
+ /**
+ * Keyboard shortcut education would be provided for All Apps. Touchpad gesture education would
+ * be provided for the rest of the gesture types (i.e. Home, Overview, Back). This method maps
+ * gesture to its target education device.
+ */
+ private fun getTargetDevice(gestureType: GestureType) =
+ when (gestureType) {
+ ALL_APPS -> KEYBOARD
+ else -> TOUCHPAD
+ }
+
+ private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean {
+ val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false
+ return clock
+ .instant()
+ .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds))
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index bb73f56..95cd9eb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -98,9 +98,6 @@
@JvmField
val AUTO_PIN_CONFIRMATION = releasedFlag("auto_pin_confirmation", "auto_pin_confirmation")
- // TODO(b/262859270): Tracking Bug
- @JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag("falsing_off_for_unfolded")
-
/** Enables code to show contextual loyalty cards in wallet entrypoints */
// TODO(b/294110497): Tracking Bug
@JvmField
@@ -203,12 +200,6 @@
@JvmField
val INCOMPATIBLE_CHARGING_BATTERY_ICON = releasedFlag("incompatible_charging_battery_icon")
- // TODO(b/293585143): Tracking Bug
- val INSTANT_TETHER = releasedFlag("instant_tether")
-
- // TODO(b/294588085): Tracking Bug
- val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
-
// TODO(b/290676905): Tracking Bug
val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS = releasedFlag("new_shade_carrier_group_mobile_icons")
@@ -233,9 +224,6 @@
// TODO(b/254512697): Tracking Bug
val MEDIA_TAP_TO_TRANSFER = releasedFlag("media_tap_to_transfer")
- // TODO(b/254512502): Tracking Bug
- val MEDIA_SESSION_ACTIONS = unreleasedFlag("media_session_actions")
-
// TODO(b/254512654): Tracking Bug
@JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag("dream_media_complication")
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt
index 5ea96b8..d2dc8c1 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt
@@ -16,7 +16,9 @@
package com.android.systemui.haptics.msdl.dagger
+import android.annotation.SuppressLint
import android.content.Context
+import android.os.VibratorManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.google.android.msdl.domain.MSDLPlayer
@@ -25,8 +27,12 @@
@Module
object MSDLModule {
+ @SuppressLint("NonInjectedService")
@Provides
@SysUISingleton
- fun provideMSDLPlayer(@Application context: Context): MSDLPlayer =
- MSDLPlayer.createPlayer(context)
+ fun provideMSDLPlayer(@Application context: Context): MSDLPlayer {
+ val vibratorManager =
+ context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
+ return MSDLPlayer.createPlayer(vibratorManager.defaultVibrator)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
index e8e1dd4..092a25a 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
@@ -16,22 +16,54 @@
package com.android.systemui.inputdevice.tutorial
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
import com.android.systemui.CoreStartable
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
import com.android.systemui.shared.Flags.newTouchpadGesturesTutorial
import dagger.Lazy
import javax.inject.Inject
-/** A [CoreStartable] to launch a scheduler for keyboard and touchpad education */
+/** A [CoreStartable] to launch a scheduler for keyboard and touchpad tutorial notification */
@SysUISingleton
class KeyboardTouchpadTutorialCoreStartable
@Inject
-constructor(private val tutorialSchedulerInteractor: Lazy<TutorialSchedulerInteractor>) :
- CoreStartable {
+constructor(
+ private val tutorialNotificationCoordinator: Lazy<TutorialNotificationCoordinator>,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ @Application private val applicationContext: Context,
+) : CoreStartable {
override fun start() {
if (newTouchpadGesturesTutorial()) {
- tutorialSchedulerInteractor.get().start()
+ tutorialNotificationCoordinator.get().start()
+ registerTutorialBroadcastReceiver()
}
}
+
+ private fun registerTutorialBroadcastReceiver() {
+ broadcastDispatcher.registerReceiver(
+ receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ applicationContext.startActivityAsUser(
+ Intent(
+ applicationContext,
+ KeyboardTouchpadTutorialActivity::class.java
+ ),
+ UserHandle.SYSTEM
+ )
+ }
+ },
+ filter = IntentFilter("com.android.systemui.action.KEYBOARD_TOUCHPAD_TUTORIAL"),
+ flags = Context.RECEIVER_EXPORTED,
+ user = UserHandle.ALL,
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index a8d7dad..cfc913f 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -17,9 +17,7 @@
package com.android.systemui.inputdevice.tutorial.domain.interactor
import android.os.SystemProperties
-import android.util.Log
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
@@ -31,23 +29,22 @@
import javax.inject.Inject
import kotlin.time.Duration.Companion.hours
import kotlin.time.toKotlinDuration
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.launch
/**
- * When the first time a keyboard or touchpad is connected, wait for [LAUNCH_DELAY], then launch the
- * tutorial as soon as there's a connected device
+ * When the first time a keyboard or touchpad is connected, wait for [LAUNCH_DELAY], and as soon as
+ * there's a connected device, show a notification to launch the tutorial.
*/
@SysUISingleton
class TutorialSchedulerInteractor
@Inject
constructor(
- @Background private val backgroundScope: CoroutineScope,
keyboardRepository: KeyboardRepository,
touchpadRepository: TouchpadRepository,
private val repo: TutorialSchedulerRepository
@@ -58,17 +55,6 @@
TOUCHPAD to touchpadRepository.isAnyTouchpadConnected
)
- fun start() {
- backgroundScope.launch {
- // Merging two flows to ensure that launch tutorial is launched consecutively in order
- // to avoid race condition
- merge(touchpadScheduleFlow, keyboardScheduleFlow).collect {
- val tutorialType = resolveTutorialType(it)
- launchTutorial(tutorialType)
- }
- }
- }
-
private val touchpadScheduleFlow = flow {
if (!repo.isLaunched(TOUCHPAD)) {
schedule(TOUCHPAD)
@@ -95,14 +81,19 @@
private suspend fun waitForDeviceConnection(deviceType: DeviceType) =
isAnyDeviceConnected[deviceType]!!.filter { it }.first()
- private suspend fun launchTutorial(tutorialType: TutorialType) {
- if (tutorialType == TutorialType.KEYBOARD || tutorialType == TutorialType.BOTH)
- repo.updateLaunchTime(KEYBOARD, Instant.now())
- if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH)
- repo.updateLaunchTime(TOUCHPAD, Instant.now())
- // TODO: launch tutorial
- Log.d(TAG, "Launch tutorial for $tutorialType")
- }
+ // Merging two flows ensures that tutorial is launched consecutively to avoid race condition
+ val tutorials: Flow<TutorialType> =
+ merge(touchpadScheduleFlow, keyboardScheduleFlow).map {
+ val tutorialType = resolveTutorialType(it)
+
+ // TODO: notifying time is not oobe launching time - move these updates into oobe
+ if (tutorialType == TutorialType.KEYBOARD || tutorialType == TutorialType.BOTH)
+ repo.updateLaunchTime(KEYBOARD, Instant.now())
+ if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH)
+ repo.updateLaunchTime(TOUCHPAD, Instant.now())
+
+ tutorialType
+ }
private suspend fun resolveTutorialType(deviceType: DeviceType): TutorialType {
// Resolve the type of tutorial depending on which device are connected when the tutorial is
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
new file mode 100644
index 0000000..5d9dda3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.inputdevice.tutorial.ui
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.core.app.NotificationCompat
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.Companion.TAG
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.TutorialType
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_BOTH
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_TOUCHPAD
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/** When the scheduler is due, show a notification to launch tutorial */
+@SysUISingleton
+class TutorialNotificationCoordinator
+@Inject
+constructor(
+ @Background private val backgroundScope: CoroutineScope,
+ @Application private val context: Context,
+ private val tutorialSchedulerInteractor: TutorialSchedulerInteractor,
+ private val notificationManager: NotificationManager
+) {
+ fun start() {
+ backgroundScope.launch {
+ tutorialSchedulerInteractor.tutorials.collect { showNotification(it) }
+ }
+ }
+
+ // By sharing the same tag and id, we update the content of existing notification instead of
+ // creating multiple notifications
+ private fun showNotification(tutorialType: TutorialType) {
+ if (tutorialType == TutorialType.NONE) return
+
+ if (notificationManager.getNotificationChannel(CHANNEL_ID) == null)
+ createNotificationChannel()
+
+ // Replace "System UI" app name with "Android System"
+ val extras = Bundle()
+ extras.putString(
+ Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ context.getString(com.android.internal.R.string.android_system_label)
+ )
+
+ val info = getNotificationInfo(tutorialType)!!
+ val notification =
+ NotificationCompat.Builder(context, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_settings)
+ .setContentTitle(info.title)
+ .setContentText(info.text)
+ .setContentIntent(createPendingIntent(info.type))
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setAutoCancel(true)
+ .addExtras(extras)
+ .build()
+
+ notificationManager.notify(TAG, NOTIFICATION_ID, notification)
+ }
+
+ private fun createNotificationChannel() {
+ val channel =
+ NotificationChannel(
+ CHANNEL_ID,
+ context.getString(com.android.internal.R.string.android_system_label),
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ notificationManager.createNotificationChannel(channel)
+ }
+
+ private fun createPendingIntent(tutorialType: String): PendingIntent {
+ val intent =
+ Intent(context, KeyboardTouchpadTutorialActivity::class.java).apply {
+ putExtra(INTENT_TUTORIAL_TYPE_KEY, tutorialType)
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ return PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ }
+
+ private data class NotificationInfo(val title: String, val text: String, val type: String)
+
+ private fun getNotificationInfo(tutorialType: TutorialType): NotificationInfo? =
+ when (tutorialType) {
+ TutorialType.KEYBOARD ->
+ NotificationInfo(
+ context.getString(R.string.launch_keyboard_tutorial_notification_title),
+ context.getString(R.string.launch_keyboard_tutorial_notification_content),
+ INTENT_TUTORIAL_TYPE_KEYBOARD
+ )
+ TutorialType.TOUCHPAD ->
+ NotificationInfo(
+ context.getString(R.string.launch_touchpad_tutorial_notification_title),
+ context.getString(R.string.launch_touchpad_tutorial_notification_content),
+ INTENT_TUTORIAL_TYPE_TOUCHPAD
+ )
+ TutorialType.BOTH ->
+ NotificationInfo(
+ context.getString(
+ R.string.launch_keyboard_touchpad_tutorial_notification_title
+ ),
+ context.getString(
+ R.string.launch_keyboard_touchpad_tutorial_notification_content
+ ),
+ INTENT_TUTORIAL_TYPE_BOTH
+ )
+ TutorialType.NONE -> null
+ }
+
+ companion object {
+ private const val CHANNEL_ID = "TutorialSchedulerNotificationChannel"
+ private const val NOTIFICATION_ID = 5566
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
index 8debe79..1adc285 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
@@ -54,6 +54,7 @@
const val INTENT_TUTORIAL_TYPE_KEY = "tutorial_type"
const val INTENT_TUTORIAL_TYPE_TOUCHPAD = "touchpad"
const val INTENT_TUTORIAL_TYPE_KEYBOARD = "keyboard"
+ const val INTENT_TUTORIAL_TYPE_BOTH = "both"
}
private val vm by
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 63f3d52..dcca12f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -82,6 +82,7 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.CornerRadius
@@ -92,8 +93,12 @@
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -824,9 +829,18 @@
// from the ViewModel.
var queryInternal by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
+ val focusManager = LocalFocusManager.current
LaunchedEffect(Unit) { focusRequester.requestFocus() }
SearchBar(
- modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
+ modifier =
+ Modifier.fillMaxWidth().focusRequester(focusRequester).onKeyEvent {
+ if (it.key == Key.DirectionDown) {
+ focusManager.moveFocus(FocusDirection.Down)
+ return@onKeyEvent true
+ } else {
+ return@onKeyEvent false
+ }
+ },
colors = SearchBarDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceBright),
query = queryInternal,
active = false,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 0feb5ec..67625d0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -76,10 +76,12 @@
import com.android.systemui.SystemUIApplication;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
@@ -122,8 +124,10 @@
private final PowerInteractor mPowerInteractor;
private final KeyguardInteractor mKeyguardInteractor;
private final Lazy<SceneInteractor> mSceneInteractorLazy;
+ private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractorLazy;
private final Executor mMainExecutor;
private final Lazy<KeyguardStateCallbackStartable> mKeyguardStateCallbackStartableLazy;
+ private final KeyguardStateCallbackInteractor mKeyguardStateCallbackInteractor;
private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
@@ -347,7 +351,9 @@
KeyguardEnabledInteractor keyguardEnabledInteractor,
Lazy<KeyguardStateCallbackStartable> keyguardStateCallbackStartableLazy,
KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor,
- KeyguardDismissInteractor keyguardDismissInteractor) {
+ KeyguardDismissInteractor keyguardDismissInteractor,
+ Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy,
+ KeyguardStateCallbackInteractor keyguardStateCallbackInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -360,6 +366,8 @@
mSceneInteractorLazy = sceneInteractorLazy;
mMainExecutor = mainExecutor;
mKeyguardStateCallbackStartableLazy = keyguardStateCallbackStartableLazy;
+ mKeyguardStateCallbackInteractor = keyguardStateCallbackInteractor;
+ mDeviceEntryInteractorLazy = deviceEntryInteractorLazy;
if (KeyguardWmStateRefactor.isEnabled()) {
WindowManagerLockscreenVisibilityViewBinder.bind(
@@ -451,6 +459,8 @@
checkPermission();
if (SceneContainerFlag.isEnabled()) {
mKeyguardStateCallbackStartableLazy.get().addCallback(callback);
+ } else if (KeyguardWmStateRefactor.isEnabled()) {
+ mKeyguardStateCallbackInteractor.addCallback(callback);
} else {
mKeyguardViewMediator.addStateMonitorCallback(callback);
}
@@ -484,7 +494,9 @@
public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
trace("dismiss message=" + message);
checkPermission();
- if (KeyguardWmStateRefactor.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
+ mDeviceEntryInteractorLazy.get().attemptDeviceEntry(callback);
+ } else if (KeyguardWmStateRefactor.isEnabled()) {
mKeyguardDismissInteractor.dismissKeyguardWithCallback(callback);
} else {
mKeyguardViewMediator.dismiss(callback, message);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 362e016c..df0f10a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.google.android.msdl.domain.MSDLPlayer
import dagger.Lazy
@@ -112,6 +113,7 @@
private val clockInteractor: KeyguardClockInteractor,
private val keyguardViewMediator: KeyguardViewMediator,
private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
@Main private val mainDispatcher: CoroutineDispatcher,
private val msdlPlayer: MSDLPlayer,
) : CoreStartable {
@@ -220,6 +222,7 @@
vibratorHelper,
falsingManager,
keyguardViewMediator,
+ statusBarKeyguardViewManager,
mainDispatcher,
msdlPlayer,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8c82900..3b1569d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2288,6 +2288,10 @@
}
private void updateInputRestrictedLocked() {
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ return;
+ }
+
boolean inputRestricted = isInputRestricted();
if (mInputRestricted != inputRestricted) {
mInputRestricted = inputRestricted;
@@ -3568,12 +3572,16 @@
}
return;
}
- try {
- mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(),
- mSelectedUserInteractor.getSelectedUserId());
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to set disable flags: " + flags, e);
+
+ // Handled in StatusBarDisableFlagsInteractor.
+ if (!KeyguardWmStateRefactor.isEnabled()) {
+ try {
+ mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
+ mContext.getPackageName(),
+ mSelectedUserInteractor.getSelectedUserId());
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to set disable flags: " + flags, e);
+ }
}
}
}
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/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index 7087752..ec52055 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -101,7 +101,7 @@
secureSettings
.observerFlow(
names = arrayOf(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
- userId = UserHandle.USER_SYSTEM,
+ userId = UserHandle.USER_ALL,
)
.onStart { emit(Unit) } // Forces an initial update.
.map { withContext(backgroundDispatcher) { getClockSize() } }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
index a1e4af5..b67fd4b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.repository
import android.content.Context
+import android.os.UserHandle
import android.provider.Settings
import android.view.View
import com.android.systemui.dagger.SysUISingleton
@@ -37,6 +38,7 @@
interface KeyguardSmartspaceRepository {
val bcSmartspaceVisibility: StateFlow<Int>
val isWeatherEnabled: StateFlow<Boolean>
+
fun setBcSmartspaceVisibility(visibility: Int)
}
@@ -55,7 +57,7 @@
secureSettings
.observerFlow(
names = arrayOf(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED),
- userId = userTracker.userId,
+ userId = UserHandle.USER_ALL,
)
.onStart { emit(Unit) }
.map { getLockscreenWeatherEnabled() }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index db5a63b..58c8a04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -73,7 +73,7 @@
if (SceneContainerFlag.isEnabled) return
listenForGoneToAodOrDozing()
listenForGoneToDreaming()
- listenForGoneToLockscreenOrHub()
+ listenForGoneToLockscreenOrHubOrOccluded()
listenForGoneToOccluded()
listenForGoneToDreamingLockscreenHosted()
}
@@ -89,22 +89,19 @@
*/
private fun listenForGoneToOccluded() {
scope.launch("$TAG#listenForGoneToOccluded") {
- keyguardInteractor.showDismissibleKeyguard
- .filterRelevantKeyguardState()
- .sample(keyguardInteractor.isKeyguardOccluded, ::Pair)
- .collect { (_, isKeyguardOccluded) ->
- if (isKeyguardOccluded) {
- startTransitionTo(
- KeyguardState.OCCLUDED,
- ownerReason = "Dismissible keyguard with occlusion"
- )
- }
+ keyguardInteractor.showDismissibleKeyguard.filterRelevantKeyguardState().collect {
+ if (keyguardInteractor.isKeyguardOccluded.value) {
+ startTransitionTo(
+ KeyguardState.OCCLUDED,
+ ownerReason = "Dismissible keyguard with occlusion"
+ )
}
+ }
}
}
// Primarily for when the user chooses to lock down the device
- private fun listenForGoneToLockscreenOrHub() {
+ private fun listenForGoneToLockscreenOrHubOrOccluded() {
if (KeyguardWmStateRefactor.isEnabled) {
scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
biometricSettingsRepository.isCurrentUserInLockdown
@@ -137,7 +134,7 @@
}
}
} else {
- scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
+ scope.launch("$TAG#listenForGoneToLockscreenOrHubOrOccluded") {
keyguardInteractor.isKeyguardShowing
.filterRelevantKeyguardStateAnd { isKeyguardShowing -> isKeyguardShowing }
.sample(communalSceneInteractor.isIdleOnCommunalNotEditMode, ::Pair)
@@ -145,6 +142,8 @@
val to =
if (isIdleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
+ } else if (keyguardInteractor.isKeyguardOccluded.value) {
+ KeyguardState.OCCLUDED
} else {
KeyguardState.LOCKSCREEN
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index e2bb540..7afc759 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -80,10 +80,7 @@
}
applicationScope.launch {
val refreshConfig =
- Config(
- Type.NoTransition,
- rebuildSections = listOf(smartspaceSection),
- )
+ Config(Type.NoTransition, rebuildSections = listOf(smartspaceSection))
configurationInteractor.onAnyConfigurationChange.collect {
refreshBlueprint(refreshConfig)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
index 7801c00..60c5386 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -32,6 +33,7 @@
import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolver
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -44,6 +46,7 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.stateIn
@@ -58,12 +61,14 @@
transitionInteractor: KeyguardTransitionInteractor,
val dismissInteractor: KeyguardDismissInteractor,
@Application private val applicationScope: CoroutineScope,
- sceneInteractor: SceneInteractor,
- deviceEntryInteractor: DeviceEntryInteractor,
- quickSettingsSceneFamilyResolver: QuickSettingsSceneFamilyResolver,
- notifShadeSceneFamilyResolver: NotifShadeSceneFamilyResolver,
+ sceneInteractor: dagger.Lazy<SceneInteractor>,
+ deviceEntryInteractor: dagger.Lazy<DeviceEntryInteractor>,
+ quickSettingsSceneFamilyResolver: dagger.Lazy<QuickSettingsSceneFamilyResolver>,
+ notifShadeSceneFamilyResolver: dagger.Lazy<NotifShadeSceneFamilyResolver>,
powerInteractor: PowerInteractor,
alternateBouncerInteractor: AlternateBouncerInteractor,
+ keyguardInteractor: dagger.Lazy<KeyguardInteractor>,
+ shadeInteractor: dagger.Lazy<ShadeInteractor>,
) {
val dismissAction: Flow<DismissAction> = repository.dismissAction
@@ -98,15 +103,31 @@
* device is unlocked. Else, false.
*/
private val isOnShadeWhileUnlocked: Flow<Boolean> =
- combine(
- sceneInteractor.currentScene,
- deviceEntryInteractor.isUnlocked,
- ) { scene, isUnlocked ->
- isUnlocked &&
- (quickSettingsSceneFamilyResolver.includesScene(scene) ||
- notifShadeSceneFamilyResolver.includesScene(scene))
+ if (SceneContainerFlag.isEnabled) {
+ combine(
+ sceneInteractor.get().currentScene,
+ deviceEntryInteractor.get().isUnlocked,
+ ) { scene, isUnlocked ->
+ isUnlocked &&
+ (quickSettingsSceneFamilyResolver.get().includesScene(scene) ||
+ notifShadeSceneFamilyResolver.get().includesScene(scene))
+ }
+ .distinctUntilChanged()
+ } else if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) {
+ shadeInteractor.get().isAnyExpanded.sample(
+ keyguardInteractor.get().isKeyguardDismissible
+ ) { isAnyExpanded, isKeyguardDismissible ->
+ isAnyExpanded && isKeyguardDismissible
}
- .distinctUntilChanged()
+ } else {
+ flow {
+ error(
+ "This should not be used when both SceneContainerFlag " +
+ "and ComposeBouncerFlag are disabled"
+ )
+ }
+ }
+
val executeDismissAction: Flow<() -> KeyguardDone> =
merge(
finishedTransitionToGone,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 2af95f2..2c3b481 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -119,7 +119,8 @@
is ObservableTransitionState.Idle ->
it.currentScene == Scenes.Lockscreen
is ObservableTransitionState.Transition ->
- it.fromScene == Scenes.Lockscreen || it.toScene == Scenes.Lockscreen
+ it.fromContent == Scenes.Lockscreen ||
+ it.toContent == Scenes.Lockscreen
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt
new file mode 100644
index 0000000..420fbd4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.domain.interactor
+
+import android.os.DeadObjectException
+import android.os.RemoteException
+import com.android.internal.policy.IKeyguardStateCallback
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+/**
+ * Updates KeyguardStateCallbacks provided to KeyguardService with KeyguardTransitionInteractor
+ * state.
+ *
+ * This borrows heavily from [KeyguardStateCallbackStartable], which requires Flexiglass. This class
+ * can be removed after Flexiglass launches.
+ */
+@SysUISingleton
+class KeyguardStateCallbackInteractor
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : CoreStartable {
+ private val callbacks = mutableListOf<IKeyguardStateCallback>()
+
+ override fun start() {
+ if (!KeyguardWmStateRefactor.isEnabled || SceneContainerFlag.isEnabled) {
+ return
+ }
+
+ applicationScope.launch {
+ combine(
+ selectedUserInteractor.selectedUser,
+ keyguardTransitionInteractor.currentKeyguardState,
+ ::Pair
+ ).collectLatest { (selectedUser, currentState) ->
+ val iterator = callbacks.iterator()
+ withContext(backgroundDispatcher) {
+ while (iterator.hasNext()) {
+ val callback = iterator.next()
+ try {
+ callback.onShowingStateChanged(
+ currentState != KeyguardState.GONE,
+ selectedUser
+ )
+ callback.onInputRestrictedStateChanged(
+ currentState != KeyguardState.GONE)
+ } catch (e: RemoteException) {
+ if (e is DeadObjectException) {
+ iterator.remove()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fun addCallback(callback: IKeyguardStateCallback) {
+ KeyguardWmStateRefactor.isUnexpectedlyInLegacyMode()
+ callbacks.add(callback)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index d9c48fa..25b8fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -29,6 +29,7 @@
private val auditLogger: KeyguardTransitionAuditLogger,
private val bootInteractor: KeyguardTransitionBootInteractor,
private val statusBarDisableFlagsInteractor: StatusBarDisableFlagsInteractor,
+ private val keyguardStateCallbackInteractor: KeyguardStateCallbackInteractor,
) : CoreStartable {
override fun start() {
@@ -55,6 +56,7 @@
auditLogger.start()
bootInteractor.start()
statusBarDisableFlagsInteractor.start()
+ keyguardStateCallbackInteractor.start()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index d11a41e..e19b72e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -32,6 +32,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.WithPrev
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -88,6 +89,18 @@
val transitionState: StateFlow<TransitionStep> =
transitions.stateIn(scope, SharingStarted.Eagerly, TransitionStep())
+ private val sceneTransitionPair =
+ sceneInteractor.transitionState
+ .pairwise()
+ .stateIn(
+ scope,
+ SharingStarted.Eagerly,
+ WithPrev(
+ sceneInteractor.transitionState.value,
+ sceneInteractor.transitionState.value
+ )
+ )
+
/**
* A pair of the most recent STARTED step, and the transition step immediately preceding it. The
* transition framework enforces that the previous step is either a CANCELED or FINISHED step,
@@ -194,7 +207,7 @@
}
return if (SceneContainerFlag.isEnabled) {
- flow.filter {
+ flow.filter { step ->
val fromScene =
when (edge) {
is Edge.StateToState -> edge.from?.mapToSceneContainerScene()
@@ -211,8 +224,23 @@
fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null
- return@filter (fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()) ||
+ val isTransitioningBetweenLockscreenStates =
+ fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()
+ val isTransitioningBetweenDesiredScenes =
sceneInteractor.transitionState.value.isTransitioning(fromScene, toScene)
+
+ // We can't compare the terminal step with the current sceneTransition because
+ // a) STL has no guarantee that it will settle in Idle() when finished/canceled
+ // b) Comparing to Idle(toScene) would make any other FINISHED step settling in
+ // toScene pass as well
+ val terminalStepBelongsToPreviousTransition =
+ (step.transitionState == TransitionState.FINISHED ||
+ step.transitionState == TransitionState.CANCELED) &&
+ sceneTransitionPair.value.previousValue.isTransitioning(fromScene, toScene)
+
+ return@filter isTransitioningBetweenLockscreenStates ||
+ isTransitioningBetweenDesiredScenes ||
+ terminalStepBelongsToPreviousTransition
}
} else {
flow
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
index e00e33d..43aab35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
@@ -39,12 +39,14 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessModel
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -75,62 +77,66 @@
private val disableToken: IBinder = Binder()
private val disableFlagsForUserId =
- combine(
- selectedUserInteractor.selectedUser,
- keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to },
- deviceConfigInteractor.property(
- namespace = DeviceConfig.NAMESPACE_SYSTEMUI,
- name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
- default = true,
- ),
- navigationInteractor.isGesturalMode,
- authenticationInteractor.authenticationMethod,
- powerInteractor.detailedWakefulness,
- ) { values ->
- val selectedUserId = values[0] as Int
- val startedState = values[1] as KeyguardState
- val isShowHomeOverLockscreen = values[2] as Boolean
- val isGesturalMode = values[3] as Boolean
- val authenticationMethod = values[4] as AuthenticationMethodModel
- val wakefulnessModel = values[5] as WakefulnessModel
- val isOccluded = startedState == KeyguardState.OCCLUDED
+ if (!KeyguardWmStateRefactor.isEnabled || SceneContainerFlag.isEnabled) {
+ flowOf(Pair(0, StatusBarManager.DISABLE_NONE))
+ } else {
+ combine(
+ selectedUserInteractor.selectedUser,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to },
+ deviceConfigInteractor.property(
+ namespace = DeviceConfig.NAMESPACE_SYSTEMUI,
+ name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+ default = true,
+ ),
+ navigationInteractor.isGesturalMode,
+ authenticationInteractor.authenticationMethod,
+ powerInteractor.detailedWakefulness,
+ ) { values ->
+ val selectedUserId = values[0] as Int
+ val startedState = values[1] as KeyguardState
+ val isShowHomeOverLockscreen = values[2] as Boolean
+ val isGesturalMode = values[3] as Boolean
+ val authenticationMethod = values[4] as AuthenticationMethodModel
+ val wakefulnessModel = values[5] as WakefulnessModel
+ val isOccluded = startedState == KeyguardState.OCCLUDED
- val hideHomeAndRecentsForBouncer =
- startedState == KeyguardState.PRIMARY_BOUNCER ||
- startedState == KeyguardState.ALTERNATE_BOUNCER
- val isKeyguardShowing = startedState != KeyguardState.GONE
- val isPowerGestureIntercepted =
- with(wakefulnessModel) {
- isAwake() &&
- powerButtonLaunchGestureTriggered &&
- lastSleepReason == WakeSleepReason.POWER_BUTTON
+ val hideHomeAndRecentsForBouncer =
+ startedState == KeyguardState.PRIMARY_BOUNCER ||
+ startedState == KeyguardState.ALTERNATE_BOUNCER
+ val isKeyguardShowing = startedState != KeyguardState.GONE
+ val isPowerGestureIntercepted =
+ with(wakefulnessModel) {
+ isAwake() &&
+ powerButtonLaunchGestureTriggered &&
+ lastSleepReason == WakeSleepReason.POWER_BUTTON
+ }
+
+ var flags = StatusBarManager.DISABLE_NONE
+
+ if (hideHomeAndRecentsForBouncer || (isKeyguardShowing && !isOccluded)) {
+ if (!isShowHomeOverLockscreen || !isGesturalMode) {
+ flags = flags or StatusBarManager.DISABLE_HOME
+ }
+ flags = flags or StatusBarManager.DISABLE_RECENT
}
- var flags = StatusBarManager.DISABLE_NONE
-
- if (hideHomeAndRecentsForBouncer || (isKeyguardShowing && !isOccluded)) {
- if (!isShowHomeOverLockscreen || !isGesturalMode) {
- flags = flags or StatusBarManager.DISABLE_HOME
+ if (
+ isPowerGestureIntercepted &&
+ isOccluded &&
+ authenticationMethod.isSecure &&
+ deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+ ) {
+ flags = flags or StatusBarManager.DISABLE_RECENT
}
- flags = flags or StatusBarManager.DISABLE_RECENT
- }
- if (
- isPowerGestureIntercepted &&
- isOccluded &&
- authenticationMethod.isSecure &&
- deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
- ) {
- flags = flags or StatusBarManager.DISABLE_RECENT
+ selectedUserId to flags
}
-
- selectedUserId to flags
- }
- .distinctUntilChanged()
+ .distinctUntilChanged()
+ }
@SuppressLint("WrongConstant", "NonInjectedService")
override fun start() {
- if (!KeyguardWmStateRefactor.isEnabled) {
+ if (!KeyguardWmStateRefactor.isEnabled || SceneContainerFlag.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index ac87400..a09cd7c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -127,8 +127,8 @@
when (transitionState) {
is ObservableTransitionState.Transition ->
when {
- transitionState.fromScene == Scenes.Lockscreen &&
- transitionState.toScene == Scenes.Gone ->
+ transitionState.fromContent == Scenes.Lockscreen &&
+ transitionState.toContent == Scenes.Gone ->
sceneInteractor
.get()
.isTransitionUserInputOngoing
@@ -139,8 +139,8 @@
flowOf(true)
}
}
- transitionState.fromScene == Scenes.Bouncer &&
- transitionState.toScene == Scenes.Gone ->
+ transitionState.fromContent == Scenes.Bouncer &&
+ transitionState.toContent == Scenes.Gone ->
transitionState.progress.map { progress ->
progress >
FromPrimaryBouncerTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index ffd7812..f3bb829 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -111,7 +111,7 @@
if (currentTransitionId == null) return
if (prevTransition !is ObservableTransitionState.Transition) return
- if (idle.currentScene == prevTransition.toScene) {
+ if (idle.currentScene == prevTransition.toContent) {
finishCurrentTransition()
} else {
val targetState =
@@ -150,7 +150,7 @@
}
private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
- if (transition.fromScene == Scenes.Lockscreen) {
+ if (transition.fromContent == Scenes.Lockscreen) {
if (currentTransitionId != null) {
val currentToState =
internalTransitionInteractor.currentTransitionInfoInternal.value.to
@@ -160,7 +160,7 @@
}
startTransitionFromLockscreen()
collectProgress(transition)
- } else if (transition.toScene == Scenes.Lockscreen) {
+ } else if (transition.toContent == Scenes.Lockscreen) {
if (currentTransitionId != null) {
transitionKtfTo(UNDEFINED)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 91a7f7f..7696273 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -42,6 +42,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scrim.ScrimView
@@ -191,6 +192,7 @@
optionallyAddUdfpsViews(
view = view,
+ logger = alternateBouncerDependencies.logger,
udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel,
udfpsA11yOverlayViewModel =
alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel,
@@ -248,6 +250,7 @@
private fun optionallyAddUdfpsViews(
view: ConstraintLayout,
+ logger: LongPressHandlingViewLogger,
udfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
udfpsA11yOverlayViewModel: Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
) {
@@ -276,7 +279,7 @@
var udfpsView = view.getViewById(udfpsViewId)
if (udfpsView == null) {
udfpsView =
- DeviceEntryIconView(view.context, null).apply {
+ DeviceEntryIconView(view.context, null, logger = logger).apply {
id = udfpsViewId
contentDescription =
context.resources.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 4d6577c..b951b73 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint
import android.content.res.ColorStateList
+import android.util.Log
import android.util.StateSet
import android.view.HapticFeedbackConstants
import android.view.View
@@ -83,6 +84,11 @@
if (
!isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
) {
+ Log.d(
+ TAG,
+ "Long press rejected because it is not a11yAction " +
+ "and it is a falseLongTap"
+ )
return
}
vibratorHelper.performHapticFeedback(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index bec8f3d..f1b9cba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -93,7 +93,7 @@
blueprint.applyConstraints(this)
}
- logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
+ logAlphaVisibilityScaleOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
}
@@ -115,7 +115,7 @@
clone(constraintLayout)
blueprint.applyConstraints(this)
}
- logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
+ logAlphaVisibilityScaleOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
}
@@ -124,7 +124,7 @@
}
}
- private fun logAlphaVisibilityOfAppliedConstraintSet(
+ private fun logAlphaVisibilityScaleOfAppliedConstraintSet(
cs: ConstraintSet,
viewModel: KeyguardClockViewModel
) {
@@ -136,12 +136,15 @@
Log.i(
TAG,
"applyCsToSmallClock: vis=${cs.getVisibility(smallClockViewId)} " +
- "alpha=${cs.getConstraint(smallClockViewId).propertySet.alpha}"
+ "alpha=${cs.getConstraint(smallClockViewId).propertySet.alpha} " +
+ "scale=${cs.getConstraint(smallClockViewId).transform.scaleX} "
)
Log.i(
TAG,
"applyCsToLargeClock: vis=${cs.getVisibility(largeClockViewId)} " +
- "alpha=${cs.getConstraint(largeClockViewId).propertySet.alpha}"
+ "alpha=${cs.getConstraint(largeClockViewId).propertySet.alpha} " +
+ "scale=${cs.getConstraint(largeClockViewId).transform.scaleX} " +
+ "pivotX=${cs.getConstraint(largeClockViewId).transform.transformPivotX} "
)
Log.i(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
index 74a7262..69cb6a9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
@@ -17,11 +17,11 @@
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.CoreStartable
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import javax.inject.Inject
@@ -41,7 +41,7 @@
) : CoreStartable {
override fun start() {
- if (!SceneContainerFlag.isEnabled) {
+ if (!ComposeBouncerFlags.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
index ac24591..b55f813 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
@@ -18,13 +18,12 @@
import com.android.keyguard.ViewMediatorCallback
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.CoreStartable
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -40,11 +39,10 @@
private val viewMediatorCallback: ViewMediatorCallback,
@Application private val scope: CoroutineScope,
private val keyguardLogger: KeyguardLogger,
- private val featureFlags: FeatureFlagsClassic,
) : CoreStartable {
override fun start() {
- if (!SceneContainerFlag.isEnabled) {
+ if (!ComposeBouncerFlags.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index f2d39da..ecfabc3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -18,13 +18,12 @@
import android.annotation.SuppressLint
import android.graphics.PointF
+import android.view.InputDevice
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewPropertyAnimator
-import androidx.core.animation.CycleInterpolator
-import androidx.core.animation.ObjectAnimator
-import com.android.systemui.res.R
+import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.view.rawDistanceFrom
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
@@ -71,11 +70,10 @@
// Moving too far while performing a long-press gesture cancels that
// gesture.
if (
- event
- .rawDistanceFrom(
- downDisplayCoords.x,
- downDisplayCoords.y,
- ) > ViewConfiguration.getTouchSlop()
+ event.rawDistanceFrom(
+ downDisplayCoords.x,
+ downDisplayCoords.y,
+ ) > ViewConfiguration.getTouchSlop()
) {
cancel()
}
@@ -151,10 +149,14 @@
event: MotionEvent,
pointerIndex: Int = 0,
): Boolean {
- return when (event.getToolType(pointerIndex)) {
- MotionEvent.TOOL_TYPE_STYLUS -> true
- MotionEvent.TOOL_TYPE_MOUSE -> true
- else -> false
+ return if (Flags.nonTouchscreenDevicesBypassFalsing()) {
+ event.device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == false
+ } else {
+ when (event.getToolType(pointerIndex)) {
+ MotionEvent.TOOL_TYPE_STYLUS -> true
+ MotionEvent.TOOL_TYPE_MOUSE -> true
+ else -> false
+ }
}
}
}
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/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index a7a8321..ed82159 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -22,9 +22,10 @@
import android.annotation.SuppressLint
import android.graphics.Point
import android.graphics.Rect
-import android.os.VibrationAttributes
import android.util.Log
import android.view.HapticFeedbackConstants
+import android.view.InputDevice
+import android.view.MotionEvent
import android.view.View
import android.view.View.OnLayoutChangeListener
import android.view.View.VISIBLE
@@ -41,6 +42,8 @@
import com.android.app.tracing.coroutines.launch
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
+import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags
import com.android.systemui.Flags.msdlFeedback
import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.Icon
@@ -73,6 +76,7 @@
import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.temporarydisplay.ViewPriority
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
@@ -82,7 +86,6 @@
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import com.google.android.msdl.data.model.MSDLToken
-import com.google.android.msdl.domain.InteractionProperties
import com.google.android.msdl.domain.MSDLPlayer
import kotlin.math.min
import kotlinx.coroutines.CoroutineDispatcher
@@ -116,6 +119,7 @@
vibratorHelper: VibratorHelper?,
falsingManager: FalsingManager?,
keyguardViewMediator: KeyguardViewMediator?,
+ statusBarKeyguardViewManager: StatusBarKeyguardViewManager?,
mainImmediateDispatcher: CoroutineDispatcher,
msdlPlayer: MSDLPlayer?,
): DisposableHandle {
@@ -125,12 +129,30 @@
if (KeyguardBottomAreaRefactor.isEnabled) {
disposables +=
view.onTouchListener { _, event ->
+ var consumed = false
if (falsingManager?.isFalseTap(FalsingManager.LOW_PENALTY) == false) {
+ // signifies a primary button click down has reached keyguardrootview
+ // we need to return true here otherwise an ACTION_UP will never arrive
+ if (Flags.nonTouchscreenDevicesBypassFalsing()) {
+ if (
+ event.action == MotionEvent.ACTION_DOWN &&
+ event.buttonState == MotionEvent.BUTTON_PRIMARY &&
+ !event.isTouchscreenSource()
+ ) {
+ consumed = true
+ } else if (
+ event.action == MotionEvent.ACTION_UP &&
+ !event.isTouchscreenSource()
+ ) {
+ statusBarKeyguardViewManager?.showBouncer(true)
+ consumed = true
+ }
+ }
viewModel.setRootViewLastTapPosition(
Point(event.x.toInt(), event.y.toInt())
)
}
- false
+ consumed
}
}
@@ -358,19 +380,14 @@
launch {
deviceEntryHapticsInteractor.playSuccessHaptic.collect {
if (msdlFeedback()) {
- val properties =
- object : InteractionProperties {
- override val vibrationAttributes: VibrationAttributes =
- VibrationAttributes.createForUsage(
- VibrationAttributes.USAGE_HARDWARE_FEEDBACK
- )
- }
- msdlPlayer?.playToken(MSDLToken.UNLOCK, properties)
+ msdlPlayer?.playToken(
+ MSDLToken.UNLOCK,
+ authInteractionProperties
+ )
} else {
vibratorHelper.performHapticFeedback(
view,
- HapticFeedbackConstants.CONFIRM,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
)
}
}
@@ -379,19 +396,14 @@
launch {
deviceEntryHapticsInteractor.playErrorHaptic.collect {
if (msdlFeedback()) {
- val properties =
- object : InteractionProperties {
- override val vibrationAttributes: VibrationAttributes =
- VibrationAttributes.createForUsage(
- VibrationAttributes.USAGE_HARDWARE_FEEDBACK
- )
- }
- msdlPlayer?.playToken(MSDLToken.FAILURE, properties)
+ msdlPlayer?.playToken(
+ MSDLToken.FAILURE,
+ authInteractionProperties
+ )
} else {
vibratorHelper.performHapticFeedback(
view,
- HapticFeedbackConstants.REJECT,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ HapticFeedbackConstants.BIOMETRIC_REJECT,
)
}
}
@@ -648,6 +660,10 @@
}
}
+ private fun MotionEvent.isTouchscreenSource(): Boolean {
+ return device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == true
+ }
+
private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator =
setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f)
@@ -662,6 +678,7 @@
private val lockIcon = R.id.lock_icon_view
private val deviceEntryIcon = R.id.device_entry_icon_view
private val nsslPlaceholderId = R.id.nssl_placeholder
+ private val authInteractionProperties = AuthInteractionProperties()
private const val ID = "occluding_app_device_entry_unlock_msg"
private const val AOD_ICONS_APPEAR_DURATION: Long = 200
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index f581a2e..0b8f741 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -417,6 +417,7 @@
null, // device entry haptics not required for preview mode
null, // falsing manager not required for preview mode
null, // keyguard view mediator is not required for preview mode
+ null, // primary bouncer interactor is not required for preview mode
mainDispatcher,
null,
)
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/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 3e6d5da..8d2e939 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -23,6 +23,7 @@
import android.util.StateSet
import android.view.Gravity
import android.view.View
+import android.view.ViewConfiguration
import android.view.ViewGroup
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.FrameLayout
@@ -31,6 +32,7 @@
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.res.R
class DeviceEntryIconView
@@ -39,8 +41,17 @@
context: Context,
attrs: AttributeSet?,
defStyleAttrs: Int = 0,
+ logger: LongPressHandlingViewLogger? = null,
) : FrameLayout(context, attrs, defStyleAttrs) {
- val longPressHandlingView: LongPressHandlingView = LongPressHandlingView(context, attrs)
+
+ val longPressHandlingView: LongPressHandlingView =
+ LongPressHandlingView(
+ context = context,
+ attrs = attrs,
+ longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() },
+ allowedTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(),
+ logger = logger,
+ )
val iconView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_fg }
val bgView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_bg }
val aodFpDrawable: LottieDrawable = LottieDrawable()
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/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index c8fe55d..be6b0eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -59,6 +59,16 @@
alpha: Float,
) = views.forEach { view -> this.setAlpha(view.id, alpha) }
+internal fun ConstraintSet.setScaleX(
+ views: Iterable<View>,
+ alpha: Float,
+) = views.forEach { view -> this.setScaleX(view.id, alpha) }
+
+internal fun ConstraintSet.setScaleY(
+ views: Iterable<View>,
+ alpha: Float,
+) = views.forEach { view -> this.setScaleY(view.id, alpha) }
+
@SysUISingleton
class ClockSection
@Inject
@@ -125,6 +135,9 @@
setAlpha(getNonTargetClockFace(clock).views, 0F)
if (!keyguardClockViewModel.isLargeClockVisible.value) {
connect(sharedR.id.bc_smartspace_view, TOP, sharedR.id.date_smartspace_view, BOTTOM)
+ } else {
+ setScaleX(getTargetClockFace(clock).views, rootViewModel.burnInModel.value.scale)
+ setScaleY(getTargetClockFace(clock).views, rootViewModel.burnInModel.value.scale)
}
}
}
@@ -205,6 +218,9 @@
create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
setGuidelineBegin(R.id.small_clock_guideline_top, smallClockTopMargin)
connect(R.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM)
+
+ // Explicitly clear pivot to force recalculate pivot instead of using legacy value
+ setTransformPivot(R.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
}
constrainWeatherClockDateIconsBarrier(constraints)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 51230dd..782d37b1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -42,6 +42,9 @@
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LongPressHandlingViewLogger
+import com.android.systemui.log.dagger.LongPressTouchLog
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
@@ -69,6 +72,7 @@
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
private val vibratorHelper: Lazy<VibratorHelper>,
+ @LongPressTouchLog private val logBuffer: LogBuffer,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
private var disposableHandle: DisposableHandle? = null
@@ -88,7 +92,16 @@
val view =
if (DeviceEntryUdfpsRefactor.isEnabled) {
- DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
+ DeviceEntryIconView(
+ context,
+ null,
+ logger =
+ LongPressHandlingViewLogger(
+ logBuffer = logBuffer,
+ TAG
+ )
+ )
+ .apply { id = deviceEntryIconViewId }
} else {
// KeyguardBottomAreaRefactor.isEnabled or MigrateClocksToBlueprint.isEnabled
LockIconView(context, null).apply { id = R.id.lock_icon_view }
@@ -258,4 +271,8 @@
}
}
}
+
+ companion object {
+ private const val TAG = "DefaultDeviceEntrySection"
+ }
}
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/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 55fc718..99160f8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -127,6 +127,7 @@
// migrate addSmartspaceView from KeyguardClockSwitchController
constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT)
+ constrainWidth(sharedR.id.bc_smartspace_view, ConstraintSet.MATCH_CONSTRAINT)
connect(
sharedR.id.bc_smartspace_view,
ConstraintSet.START,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
index b432417..9f8e9c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
@@ -18,6 +18,9 @@
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LongPressHandlingViewLogger
+import com.android.systemui.log.dagger.LongPressTouchLog
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.statusbar.gesture.TapGestureDetector
import dagger.Lazy
@@ -37,4 +40,11 @@
Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
val messageAreaViewModel: AlternateBouncerMessageAreaViewModel,
val powerInteractor: PowerInteractor,
-)
+ @LongPressTouchLog private val touchLogBuffer: LogBuffer,
+) {
+ val logger: LongPressHandlingViewLogger =
+ LongPressHandlingViewLogger(logBuffer = touchLogBuffer, TAG)
+ companion object {
+ private const val TAG = "AlternateBouncer"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 8485a26..1edfec8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -18,6 +18,7 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -25,7 +26,6 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -51,7 +51,7 @@
) {
/** Common fade for scrim alpha values during *BOUNCER->GONE */
fun scrimAlpha(duration: Duration, fromState: KeyguardState): Flow<ScrimAlpha> {
- return if (SceneContainerFlag.isEnabled) {
+ return if (ComposeBouncerFlags.isEnabled) {
keyguardDismissActionInteractor
.get()
.willAnimateDismissActionOnLockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 9f68210..a021de4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -43,23 +43,22 @@
val deviceEntryIconViewModel: DeviceEntryIconViewModel,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
configurationInteractor: ConfigurationInteractor,
- lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
- aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
- goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
- primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
- occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
- occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
- dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
- goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
- goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
- primaryBouncerToDozingTransitionViewModel: PrimaryBouncerToDozingTransitionViewModel,
- lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
- dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
alternateBouncerToDozingTransitionViewModel: AlternateBouncerToDozingTransitionViewModel,
+ aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+ dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
dreamingToAodTransitionViewModel: DreamingToAodTransitionViewModel,
- primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
+ dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+ goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+ goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
+ goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
+ lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+ occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
occludedToDozingTransitionViewModel: OccludedToDozingTransitionViewModel,
+ occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+ primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
+ primaryBouncerToDozingTransitionViewModel: PrimaryBouncerToDozingTransitionViewModel,
+ primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
) {
val color: Flow<Int> =
deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground ->
@@ -98,7 +97,6 @@
goneToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
goneToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
primaryBouncerToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
- lockscreenToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
dozingToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
alternateBouncerToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
dreamingToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
index f33752f..12bcc7e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
+import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -55,8 +56,18 @@
var currentAlpha = 0f
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
- startTime = 100.milliseconds, // Wait for the light reveal to "hit" the LS elements.
- onStart = { currentAlpha = viewState.alpha() },
+ startTime = if (lightRevealMigration()) {
+ 100.milliseconds // Wait for the light reveal to "hit" the LS elements.
+ } else {
+ 0.milliseconds
+ },
+ onStart = {
+ if (lightRevealMigration()) {
+ currentAlpha = viewState.alpha()
+ } else {
+ currentAlpha = 0f
+ }
+ },
onStep = { MathUtils.lerp(currentAlpha, 0f, it) },
onCancel = { 0f },
)
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 fe4ebfe..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
@@ -20,7 +20,6 @@
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.FlowTracing.traceEmissionCount
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.NewPickerUiKeyguardPreview
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -29,6 +28,7 @@
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shared.Flags
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
@@ -169,7 +169,7 @@
/** An observable for the view-model of the "start button" quick affordance. */
val startButton: Flow<KeyguardQuickAffordanceViewModel> =
- if (NewPickerUiKeyguardPreview.isEnabled) {
+ if (Flags.newCustomizationPickerUi()) {
previewAffordances.flatMapLatestConflated {
button(
position = KeyguardQuickAffordancePosition.BOTTOM_START,
@@ -177,14 +177,20 @@
)
}
} 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> =
- if (NewPickerUiKeyguardPreview.isEnabled) {
+ if (Flags.newCustomizationPickerUi()) {
previewAffordances.flatMapLatestConflated {
button(
position = KeyguardQuickAffordancePosition.BOTTOM_END,
@@ -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/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index ebdcaa0..eaa61a1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -34,20 +34,18 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
-import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.util.kotlin.BooleanFlowOperators.any
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
@@ -86,6 +84,7 @@
private val communalInteractor: CommunalInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
+ notificationShadeWindowModel: NotificationShadeWindowModel,
private val alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
@@ -197,37 +196,18 @@
.distinctUntilChanged()
/**
- * Keyguard states which should fully hide the keyguard.
- *
- * Note: [GONE] is not included as it is handled separately.
- */
- private val hiddenKeyguardStates = listOf(OCCLUDED, DREAMING, GLANCEABLE_HUB)
-
- /**
* Keyguard should not show if fully transitioned into a hidden keyguard state or if
* transitioning between hidden states.
*/
private val hideKeyguard: Flow<Boolean> =
- (hiddenKeyguardStates.map { state ->
- keyguardTransitionInteractor
- .transitionValue(state)
- .map { it == 1f }
- .onStart { emit(false) }
- } +
- listOf(
- communalInteractor.isIdleOnCommunal,
- keyguardTransitionInteractor
- .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
- .map { it == 1f }
- .onStart { emit(false) },
- keyguardTransitionInteractor
- .isInTransitionWhere(
- fromStatePredicate = { hiddenKeyguardStates.contains(it) },
- toStatePredicate = { hiddenKeyguardStates.contains(it) },
- )
- .onStart { emit(false) },
- ))
- .any()
+ anyOf(
+ notificationShadeWindowModel.isKeyguardOccluded,
+ communalInteractor.isIdleOnCommunal,
+ keyguardTransitionInteractor
+ .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
+ .map { it == 1f }
+ .onStart { emit(false) },
+ )
/** Last point that the root view was tapped */
val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
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/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt
deleted file mode 100644
index 2819e61..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.util.kotlin.filterValuesNotNull
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-
-/** Models UI state and handles user input for the lockscreen scene. */
-class LockscreenSceneActionsViewModel
-@AssistedInject
-constructor(
- private val deviceEntryInteractor: DeviceEntryInteractor,
- private val communalInteractor: CommunalInteractor,
- private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
-
- override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
- shadeInteractor.isShadeTouchable
- .flatMapLatest { isShadeTouchable ->
- if (!isShadeTouchable) {
- flowOf(emptyMap())
- } else {
- combine(
- deviceEntryInteractor.isUnlocked,
- communalInteractor.isCommunalAvailable,
- shadeInteractor.shadeMode,
- ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
- val notifShadeSceneKey =
- UserActionResult(
- toScene = SceneFamilies.NotifShade,
- transitionKey =
- ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
- )
-
- mapOf(
- Swipe.Left to
- UserActionResult(Scenes.Communal).takeIf {
- isCommunalAvailable
- },
- Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer,
-
- // Swiping down from the top edge goes to QS (or shade if in split
- // shade mode).
- swipeDownFromTop(pointerCount = 1) to
- if (shadeMode is ShadeMode.Single) {
- UserActionResult(Scenes.QuickSettings)
- } else {
- notifShadeSceneKey
- },
-
- // TODO(b/338577208): Remove once we add Dual Shade invocation zones
- swipeDownFromTop(pointerCount = 2) to
- UserActionResult(
- toScene = SceneFamilies.QuickSettings,
- transitionKey =
- ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
- ),
-
- // Swiping down, not from the edge, always navigates to the notif
- // shade scene.
- swipeDown(pointerCount = 1) to notifShadeSceneKey,
- swipeDown(pointerCount = 2) to notifShadeSceneKey,
- )
- .filterValuesNotNull()
- }
- }
- }
- .collect { setActions(it) }
- }
-
- private fun swipeDownFromTop(pointerCount: Int): Swipe {
- return Swipe(
- SwipeDirection.Down,
- fromSource = Edge.Top,
- pointerCount = pointerCount,
- )
- }
-
- private fun swipeDown(pointerCount: Int): Swipe {
- return Swipe(
- SwipeDirection.Down,
- pointerCount = pointerCount,
- )
- }
-
- @AssistedFactory
- interface Factory {
- fun create(): LockscreenSceneActionsViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index 27a1f7a..d3eefca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -45,13 +45,7 @@
edge = Edge.create(from = LOCKSCREEN, to = DOZING),
)
- val lockscreenAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- duration = 250.milliseconds,
- onStep = { 1 - it },
- onFinish = { 1f },
- onCancel = { 1f },
- )
+ val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f)
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
@@ -61,18 +55,16 @@
onCancel = { 1f },
)
- val deviceEntryBackgroundViewAlpha: Flow<Float> =
- transitionAnimation.immediatelyTransitionTo(0f)
-
override val deviceEntryParentViewAlpha: Flow<Float> =
deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
isUdfpsEnrolledAndEnabled ->
- transitionAnimation.immediatelyTransitionTo(
- if (isUdfpsEnrolledAndEnabled) {
- 1f
- } else {
- 0f
- }
- )
+ if (isUdfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ transitionAnimation.sharedFlow(
+ duration = 250.milliseconds,
+ onStep = { 1f - it },
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt
new file mode 100644
index 0000000..ecae079
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+
+/** Models UI state and handles user input for the lockscreen scene. */
+class LockscreenUserActionsViewModel
+@AssistedInject
+constructor(
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+ private val communalInteractor: CommunalInteractor,
+ private val shadeInteractor: ShadeInteractor,
+) : UserActionsViewModel() {
+
+ override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+ shadeInteractor.isShadeTouchable
+ .flatMapLatest { isShadeTouchable ->
+ if (!isShadeTouchable) {
+ return@flatMapLatest flowOf(emptyMap())
+ }
+
+ combine(
+ deviceEntryInteractor.isUnlocked,
+ communalInteractor.isCommunalAvailable,
+ shadeInteractor.shadeMode,
+ ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
+ buildList {
+ if (isCommunalAvailable) {
+ add(Swipe.Left to Scenes.Communal)
+ }
+
+ add(Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer)
+
+ addAll(
+ when (shadeMode) {
+ ShadeMode.Single -> fullscreenShadeActions()
+ ShadeMode.Split ->
+ fullscreenShadeActions(transitionKey = ToSplitShade)
+ ShadeMode.Dual -> dualShadeActions()
+ }
+ )
+ }
+ .associate { it }
+ }
+ }
+ .collect { setActions(it) }
+ }
+
+ private fun fullscreenShadeActions(
+ transitionKey: TransitionKey? = null
+ ): Array<Pair<UserAction, UserActionResult>> {
+ val notifShadeSceneKey = UserActionResult(SceneFamilies.NotifShade, transitionKey)
+ val qsShadeSceneKey = UserActionResult(SceneFamilies.QuickSettings, transitionKey)
+ return arrayOf(
+ // Swiping down, not from the edge, always goes to shade.
+ Swipe.Down to notifShadeSceneKey,
+ swipeDown(pointerCount = 2) to notifShadeSceneKey,
+ // Swiping down from the top edge goes to QS.
+ swipeDownFromTop(pointerCount = 1) to qsShadeSceneKey,
+ swipeDownFromTop(pointerCount = 2) to qsShadeSceneKey,
+ )
+ }
+
+ private fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> {
+ return arrayOf(
+ Swipe.Down to Overlays.NotificationsShade,
+ Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
+ Overlays.QuickSettingsShade,
+ )
+ }
+
+ private fun swipeDownFromTop(pointerCount: Int): Swipe {
+ return Swipe(SwipeDirection.Down, fromSource = Edge.Top, pointerCount = pointerCount)
+ }
+
+ private fun swipeDown(pointerCount: Int): Swipe {
+ return Swipe(SwipeDirection.Down, pointerCount = pointerCount)
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): LockscreenUserActionsViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 8811908..17c678e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
@@ -25,7 +26,6 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.SysuiStatusBarStateController
import dagger.Lazy
import javax.inject.Inject
@@ -85,7 +85,7 @@
/** Bouncer container alpha */
val bouncerAlpha: Flow<Float> =
- if (SceneContainerFlag.isEnabled) {
+ if (ComposeBouncerFlags.isEnabled) {
keyguardDismissActionInteractor
.get()
.willAnimateDismissActionOnLockscreen
@@ -110,7 +110,7 @@
/** Lockscreen alpha */
val lockscreenAlpha: Flow<Float> =
- if (SceneContainerFlag.isEnabled) {
+ if (ComposeBouncerFlags.isEnabled) {
keyguardDismissActionInteractor
.get()
.willAnimateDismissActionOnLockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index c5909ed..75e3871 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -107,6 +107,8 @@
}
}
+ // TODO(b/365182034): move to interactor, add as dependency of SideFpsOverlayInteractor when
+ // rest to unlock feature is implemented
val isVisible: Flow<Boolean> = _visible.asStateFlow()
val progress: Flow<Float> = _progress.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/log/LongPressHandlingViewLogger.kt b/packages/SystemUI/src/com/android/systemui/log/LongPressHandlingViewLogger.kt
new file mode 100644
index 0000000..4ff8118
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LongPressHandlingViewLogger.kt
@@ -0,0 +1,80 @@
+/*
+ * 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
+
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.google.errorprone.annotations.CompileTimeConstant
+
+data class LongPressHandlingViewLogger
+constructor(
+ private val logBuffer: LogBuffer,
+ @CompileTimeConstant private val tag: String = "LongPressHandlingViewLogger"
+) {
+ fun schedulingLongPress(delay: Long) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ { long1 = delay },
+ { "on MotionEvent.Down: scheduling long press activation after $long1 ms" }
+ )
+ }
+
+ fun longPressTriggered() {
+ logBuffer.log(tag, DEBUG, "long press event detected and dispatched")
+ }
+
+ fun motionEventCancelled() {
+ logBuffer.log(tag, DEBUG, "Long press may be cancelled due to MotionEventModel.Cancel")
+ }
+
+ fun dispatchingSingleTap() {
+ logBuffer.log(tag, DEBUG, "Dispatching single tap instead of long press")
+ }
+
+ fun onUpEvent(distanceMoved: Float, touchSlop: Int, gestureDuration: Long) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ {
+ double1 = distanceMoved.toDouble()
+ int1 = touchSlop
+ long1 = gestureDuration
+ },
+ {
+ "on MotionEvent.Up: distanceMoved: $double1, " +
+ "allowedTouchSlop: $int1, " +
+ "eventDuration: $long1"
+ }
+ )
+ }
+
+ fun cancelingLongPressDueToTouchSlop(distanceMoved: Float, allowedTouchSlop: Int) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ {
+ double1 = distanceMoved.toDouble()
+ int1 = allowedTouchSlop
+ },
+ {
+ "on MotionEvent.Motion: May cancel long press due to movement: " +
+ "distanceMoved: $double1, " +
+ "allowedTouchSlop: $int1 "
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerTableLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerTableLog.kt
index 08df7db..9f893e0 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerTableLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerTableLog.kt
@@ -16,10 +16,7 @@
package com.android.systemui.log.dagger
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
import javax.inject.Qualifier
/** Logger for the primary and alternative bouncers. */
-@Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) annotation class BouncerTableLog
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class BouncerTableLog
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..2053b53 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -584,7 +584,7 @@
@SysUISingleton
@KeyguardQuickAffordancesLog
public static LogBuffer provideKeyguardQuickAffordancesLogBuffer(LogBufferFactory factory) {
- return factory.create("KeyguardQuickAffordancesLog", 25);
+ return factory.create("KeyguardQuickAffordancesLog", 100);
}
/**
@@ -727,4 +727,12 @@
public static LogBuffer provideVolumeLogBuffer(LogBufferFactory factory) {
return factory.create("VolumeLog", 50);
}
+
+ /** Provides a {@link LogBuffer} for use by long touch event handlers. */
+ @Provides
+ @SysUISingleton
+ @LongPressTouchLog
+ public static LogBuffer providesLongPressTouchLog(LogBufferFactory factory) {
+ return factory.create("LongPressViewLog", 200);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/packages/SystemUI/src/com/android/systemui/log/dagger/LongPressTouchLog.kt
similarity index 72%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to packages/SystemUI/src/com/android/systemui/log/dagger/LongPressTouchLog.kt
index 3c5beeb..1163d74 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LongPressTouchLog.kt
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.systemui.log.dagger
-parcelable BubbleBarLocation;
\ No newline at end of file
+import javax.inject.Qualifier
+
+/** Log buffer for logging touch/long press events */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class LongPressTouchLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 2089cce5..89a599a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -16,19 +16,17 @@
package com.android.systemui.log.table
+import android.annotation.SuppressLint
import android.icu.text.SimpleDateFormat
import android.os.Trace
import com.android.systemui.Dumpable
import com.android.systemui.common.buffer.RingBuffer
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogcatEchoTracker
import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.log.TableLogBufferBase
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import java.util.Locale
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
/**
* A logger that logs changes in table format.
@@ -75,13 +73,12 @@
*
* @param maxSize the maximum size of the buffer. Must be > 0.
*/
+@SuppressLint("DumpableNotRegistered") // Registered as dumpable in [TableLogBufferFactory]
class TableLogBuffer(
maxSize: Int,
private val name: String,
private val systemClock: SystemClock,
private val logcatEchoTracker: LogcatEchoTracker,
- @Background private val bgDispatcher: CoroutineDispatcher,
- private val coroutineScope: CoroutineScope,
private val localLogcat: LogProxy = LogProxyDefault(),
) : Dumpable, TableLogBufferBase {
init {
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
index ff523ae1..425e674e 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -17,15 +17,11 @@
package com.android.systemui.log.table
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
import com.android.systemui.log.LogcatEchoTracker
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
@SysUISingleton
class TableLogBufferFactory
@@ -34,8 +30,6 @@
private val dumpManager: DumpManager,
private val systemClock: SystemClock,
private val logcatEchoTracker: LogcatEchoTracker,
- @Background private val bgDispatcher: CoroutineDispatcher,
- @Application private val coroutineScope: CoroutineScope,
) {
private val existingBuffers = mutableMapOf<String, TableLogBuffer>()
@@ -58,8 +52,6 @@
name,
systemClock,
logcatEchoTracker,
- bgDispatcher,
- coroutineScope,
)
dumpManager.registerTableLogBuffer(name, tableBuffer)
return tableBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 24c57be..84aae65 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -70,12 +70,14 @@
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager.Companion.isMediaNotification
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.MediaLogger
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.ui.view.MediaViewHolder
@@ -83,7 +85,7 @@
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
@@ -185,7 +187,6 @@
private val mediaDeviceManager: MediaDeviceManager,
mediaDataCombineLatest: MediaDataCombineLatest,
private val mediaDataFilter: LegacyMediaDataFilterImpl,
- private val activityStarter: ActivityStarter,
private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean,
@@ -196,6 +197,7 @@
private val smartspaceManager: SmartspaceManager?,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val mediaDataLoader: dagger.Lazy<MediaDataLoader>,
+ private val mediaLogger: MediaLogger,
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener, MediaDataManager {
companion object {
@@ -272,7 +274,6 @@
mediaDeviceManager: MediaDeviceManager,
mediaDataCombineLatest: MediaDataCombineLatest,
mediaDataFilter: LegacyMediaDataFilterImpl,
- activityStarter: ActivityStarter,
smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
clock: SystemClock,
tunerService: TunerService,
@@ -281,6 +282,7 @@
smartspaceManager: SmartspaceManager?,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
mediaDataLoader: dagger.Lazy<MediaDataLoader>,
+ mediaLogger: MediaLogger,
) : this(
context,
// Loading bitmap for UMO background can take longer time, so it cannot run on the default
@@ -300,7 +302,6 @@
mediaDeviceManager,
mediaDataCombineLatest,
mediaDataFilter,
- activityStarter,
smartspaceMediaDataProvider,
Utils.useMediaResumption(context),
Utils.useQsMediaPlayer(context),
@@ -311,6 +312,7 @@
smartspaceManager,
keyguardUpdateMonitor,
mediaDataLoader,
+ mediaLogger,
)
private val appChangeReceiver =
@@ -563,6 +565,42 @@
val resumeAction: Runnable? = currentEntry?.resumeAction
val hasCheckedForResume = currentEntry?.hasCheckedForResume == true
val active = currentEntry?.active ?: true
+ val mediaController = mediaControllerFactory.create(result.token!!)
+
+ val mediaData =
+ MediaData(
+ userId = sbn.normalizedUserId,
+ initialized = true,
+ app = result.appName,
+ appIcon = result.appIcon,
+ artist = result.artist,
+ song = result.song,
+ artwork = result.artworkIcon,
+ actions = result.actionIcons,
+ actionsToShowInCompact = result.actionsToShowInCompact,
+ semanticActions = result.semanticActions,
+ packageName = sbn.packageName,
+ token = result.token,
+ clickIntent = result.clickIntent,
+ device = result.device,
+ active = active,
+ resumeAction = resumeAction,
+ playbackLocation = result.playbackLocation,
+ notificationKey = key,
+ hasCheckedForResume = hasCheckedForResume,
+ isPlaying = result.isPlaying,
+ isClearable = !sbn.isOngoing,
+ lastActive = lastActive,
+ createdTimestampMillis = createdTimestampMillis,
+ instanceId = instanceId,
+ appUid = result.appUid,
+ isExplicit = result.isExplicit,
+ )
+
+ if (isSameMediaData(context, mediaController, mediaData, currentEntry)) {
+ mediaLogger.logDuplicateMediaNotification(key)
+ return@withContext
+ }
// We need to log the correct media added.
if (isNewlyActiveEntry) {
@@ -582,40 +620,7 @@
)
}
- withContext(mainDispatcher) {
- onMediaDataLoaded(
- key,
- oldKey,
- MediaData(
- userId = sbn.normalizedUserId,
- initialized = true,
- app = result.appName,
- appIcon = result.appIcon,
- artist = result.artist,
- song = result.song,
- artwork = result.artworkIcon,
- actions = result.actionIcons,
- actionsToShowInCompact = result.actionsToShowInCompact,
- semanticActions = result.semanticActions,
- packageName = sbn.packageName,
- token = result.token,
- clickIntent = result.clickIntent,
- device = result.device,
- active = active,
- resumeAction = resumeAction,
- playbackLocation = result.playbackLocation,
- notificationKey = key,
- hasCheckedForResume = hasCheckedForResume,
- isPlaying = result.isPlaying,
- isClearable = !sbn.isOngoing,
- lastActive = lastActive,
- createdTimestampMillis = createdTimestampMillis,
- instanceId = instanceId,
- appUid = result.appUid,
- isExplicit = result.isExplicit,
- )
- )
- }
+ withContext(mainDispatcher) { onMediaDataLoaded(key, oldKey, mediaData) }
}
/** Add a listener for changes in this class */
@@ -943,7 +948,7 @@
desc.subtitle,
desc.title,
artworkIcon,
- listOf(mediaAction),
+ listOf(),
listOf(0),
MediaButton(playOrPause = mediaAction),
packageName,
@@ -1074,13 +1079,13 @@
}
// Control buttons
- // If flag is enabled and controller has a PlaybackState, create actions from session info
+ // If controller has a PlaybackState, create actions from session info
// Otherwise, use the notification actions
- var actionIcons: List<MediaAction> = emptyList()
+ var actionIcons: List<MediaNotificationAction> = emptyList()
var actionsToShowCollapsed: List<Int> = emptyList()
val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
if (semanticActions == null) {
- val actions = createActionsFromNotification(context, activityStarter, sbn)
+ val actions = createActionsFromNotification(context, sbn)
actionIcons = actions.first
actionsToShowCollapsed = actions.second
}
@@ -1099,6 +1104,47 @@
val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
val appUid = appInfo?.uid ?: Process.INVALID_UID
+ val lastActive = systemClock.elapsedRealtime()
+ val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
+ val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
+ val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
+ val active = mediaEntries[key]?.active ?: true
+ var mediaData =
+ MediaData(
+ sbn.normalizedUserId,
+ true,
+ appName,
+ smallIcon,
+ artist,
+ song,
+ artWorkIcon,
+ actionIcons,
+ actionsToShowCollapsed,
+ semanticActions,
+ sbn.packageName,
+ token,
+ notif.contentIntent,
+ device,
+ active,
+ resumeAction = resumeAction,
+ playbackLocation = playbackLocation,
+ notificationKey = key,
+ hasCheckedForResume = hasCheckedForResume,
+ isPlaying = isPlaying,
+ isClearable = !sbn.isOngoing,
+ lastActive = lastActive,
+ createdTimestampMillis = createdTimestampMillis,
+ instanceId = instanceId,
+ appUid = appUid,
+ isExplicit = isExplicit,
+ smartspaceId = SmallHash.hash(appUid + systemClock.currentTimeMillis().toInt()),
+ )
+
+ if (isSameMediaData(context, mediaController, mediaData, currentEntry)) {
+ mediaLogger.logDuplicateMediaNotification(key)
+ return
+ }
+
if (isNewlyActiveEntry) {
logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId)
logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
@@ -1106,44 +1152,17 @@
logger.logPlaybackLocationChange(appUid, sbn.packageName, instanceId, playbackLocation)
}
- val lastActive = systemClock.elapsedRealtime()
- val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
foregroundExecutor.execute {
- val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
- val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
- val active = mediaEntries[key]?.active ?: true
- onMediaDataLoaded(
- key,
- oldKey,
- MediaData(
- sbn.normalizedUserId,
- true,
- appName,
- smallIcon,
- artist,
- song,
- artWorkIcon,
- actionIcons,
- actionsToShowCollapsed,
- semanticActions,
- sbn.packageName,
- token,
- notif.contentIntent,
- device,
- active,
- resumeAction = resumeAction,
- playbackLocation = playbackLocation,
- notificationKey = key,
- hasCheckedForResume = hasCheckedForResume,
- isPlaying = isPlaying,
- isClearable = !sbn.isOngoing,
- lastActive = lastActive,
- createdTimestampMillis = createdTimestampMillis,
- instanceId = instanceId,
- appUid = appUid,
- isExplicit = isExplicit,
+ val oldResumeAction: Runnable? = mediaEntries[key]?.resumeAction
+ val oldHasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
+ val oldActive = mediaEntries[key]?.active ?: true
+ mediaData =
+ mediaData.copy(
+ resumeAction = oldResumeAction,
+ hasCheckedForResume = oldHasCheckedForResume,
+ active = oldActive
)
- )
+ onMediaDataLoaded(key, oldKey, mediaData)
}
}
@@ -1464,7 +1483,7 @@
val updated =
data.copy(
token = null,
- actions = actions,
+ actions = listOf(),
semanticActions = MediaButton(playOrPause = resumeAction),
actionsToShowInCompact = listOf(0),
active = false,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
index 378a147..f2825d0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
@@ -33,6 +33,7 @@
import com.android.systemui.media.controls.shared.MediaControlDrawables
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
@@ -59,13 +60,14 @@
val playOrPause =
if (isConnectingState(state.state)) {
// Spinner needs to be animating to render anything. Start it here.
- val drawable = MediaControlDrawables.getProgress(context)
+ val drawable =
+ context.getDrawable(com.android.internal.R.drawable.progress_small_material)
(drawable as Animatable).start()
MediaAction(
drawable,
null, // no action to perform when clicked
context.getString(R.string.controls_media_button_connecting),
- MediaControlDrawables.getConnecting(context),
+ context.getDrawable(R.drawable.ic_media_connecting_container),
// Specify a rebind id to prevent the spinner from restarting on later binds.
com.android.internal.R.drawable.progress_small_material
)
@@ -153,18 +155,18 @@
return when (action) {
PlaybackState.ACTION_PLAY -> {
MediaAction(
- MediaControlDrawables.getPlayIcon(context),
+ context.getDrawable(R.drawable.ic_media_play),
{ controller.transportControls.play() },
context.getString(R.string.controls_media_button_play),
- MediaControlDrawables.getPlayBackground(context)
+ context.getDrawable(R.drawable.ic_media_play_container)
)
}
PlaybackState.ACTION_PAUSE -> {
MediaAction(
- MediaControlDrawables.getPauseIcon(context),
+ context.getDrawable(R.drawable.ic_media_pause),
{ controller.transportControls.pause() },
context.getString(R.string.controls_media_button_pause),
- MediaControlDrawables.getPauseBackground(context)
+ context.getDrawable(R.drawable.ic_media_pause_container)
)
}
PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
@@ -216,11 +218,10 @@
/** Generate action buttons based on notification actions */
fun createActionsFromNotification(
context: Context,
- activityStarter: ActivityStarter,
sbn: StatusBarNotification
-): Pair<List<MediaAction>, List<Int>> {
+): Pair<List<MediaNotificationAction>, List<Int>> {
val notif = sbn.notification
- val actionIcons: MutableList<MediaAction> = ArrayList()
+ val actionIcons: MutableList<MediaNotificationAction> = ArrayList()
val actions = notif.actions
var actionsToShowCollapsed =
notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList()
@@ -249,25 +250,6 @@
continue
}
- val runnable =
- action.actionIntent?.let { actionIntent ->
- Runnable {
- when {
- actionIntent.isActivity ->
- activityStarter.startPendingIntentDismissingKeyguard(
- action.actionIntent
- )
- action.isAuthenticationRequired ->
- activityStarter.dismissKeyguardThenExecute(
- { sendPendingIntent(action.actionIntent) },
- {},
- true
- )
- else -> sendPendingIntent(actionIntent)
- }
- }
- }
-
val themeText =
com.android.settingslib.Utils.getColorAttr(
context,
@@ -284,13 +266,53 @@
.setTint(themeText)
.loadDrawable(context)
- val mediaAction = MediaAction(mediaActionIcon, runnable, action.title, null)
+ val mediaAction =
+ MediaNotificationAction(
+ action.isAuthenticationRequired,
+ action.actionIntent,
+ mediaActionIcon,
+ action.title
+ )
actionIcons.add(mediaAction)
}
}
return Pair(actionIcons, actionsToShowCollapsed)
}
+/**
+ * Converts [MediaNotificationAction] list into [MediaAction] list
+ *
+ * @param actions list of [MediaNotificationAction]
+ * @param activityStarter starter for activities
+ * @return list of [MediaAction]
+ */
+fun getNotificationActions(
+ actions: List<MediaNotificationAction>,
+ activityStarter: ActivityStarter
+): List<MediaAction> {
+ return actions.map { action ->
+ val runnable =
+ action.actionIntent?.let { actionIntent ->
+ Runnable {
+ when {
+ actionIntent.isActivity ->
+ activityStarter.startPendingIntentDismissingKeyguard(
+ action.actionIntent
+ )
+ action.isAuthenticationRequired ->
+ activityStarter.dismissKeyguardThenExecute(
+ { sendPendingIntent(action.actionIntent) },
+ {},
+ true
+ )
+ else -> sendPendingIntent(actionIntent)
+ }
+ }
+ }
+ MediaAction(action.icon, runnable, action.contentDescription, background = null)
+ }
+}
+
private fun sendPendingIntent(intent: PendingIntent): Boolean {
return try {
intent.send(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
index f9fef8e..53cc15b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
@@ -54,10 +54,10 @@
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
@@ -80,7 +80,6 @@
@Application val context: Context,
@Main val mainDispatcher: CoroutineDispatcher,
@Background val backgroundScope: CoroutineScope,
- private val activityStarter: ActivityStarter,
private val mediaControllerFactory: MediaControllerFactory,
private val mediaFlags: MediaFlags,
private val imageLoader: ImageLoader,
@@ -209,15 +208,14 @@
val device: MediaDeviceData? = getDeviceInfoForRemoteCast(key, sbn)
// Control buttons
- // If flag is enabled and controller has a PlaybackState, create actions from session
- // info
+ // If controller has a PlaybackState, create actions from session info
// Otherwise, use the notification actions
- var actionIcons: List<MediaAction> = emptyList()
+ var actionIcons: List<MediaNotificationAction> = emptyList()
var actionsToShowCollapsed: List<Int> = emptyList()
val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
logD(TAG) { "Semantic actions: $semanticActions" }
if (semanticActions == null) {
- val actions = createActionsFromNotification(context, activityStarter, sbn)
+ val actions = createActionsFromNotification(context, sbn)
actionIcons = actions.first
actionsToShowCollapsed = actions.second
logD(TAG) { "[!!] Semantic actions: $semanticActions" }
@@ -329,7 +327,7 @@
artist = desc.subtitle,
song = desc.title,
artworkIcon = artworkIcon,
- actionIcons = listOf(mediaAction),
+ actionIcons = listOf(),
actionsToShowInCompact = listOf(0),
semanticActions = MediaButton(playOrPause = mediaAction),
token = token,
@@ -514,7 +512,7 @@
val artist: CharSequence?,
val song: CharSequence?,
val artworkIcon: Icon?,
- val actionIcons: List<MediaAction>,
+ val actionIcons: List<MediaNotificationAction>,
val actionsToShowInCompact: List<Int>,
val semanticActions: MediaButton?,
val token: MediaSession.Token?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 415449f..5f0a9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -71,13 +71,14 @@
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager.Companion.isMediaNotification
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
-import com.android.systemui.media.controls.shared.MediaControlDrawables
+import com.android.systemui.media.controls.shared.MediaLogger
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.ui.view.MediaViewHolder
@@ -150,6 +151,7 @@
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val mediaDataRepository: MediaDataRepository,
private val mediaDataLoader: dagger.Lazy<MediaDataLoader>,
+ private val mediaLogger: MediaLogger,
) : CoreStartable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -229,6 +231,7 @@
keyguardUpdateMonitor: KeyguardUpdateMonitor,
mediaDataRepository: MediaDataRepository,
mediaDataLoader: dagger.Lazy<MediaDataLoader>,
+ mediaLogger: MediaLogger,
) : this(
context,
applicationScope,
@@ -254,6 +257,7 @@
keyguardUpdateMonitor,
mediaDataRepository,
mediaDataLoader,
+ mediaLogger,
)
private val appChangeReceiver =
@@ -795,7 +799,7 @@
desc.subtitle,
desc.title,
artworkIcon,
- listOf(mediaAction),
+ listOf(),
listOf(0),
MediaButton(playOrPause = mediaAction),
packageName,
@@ -833,12 +837,48 @@
return@withContext
}
- val currentEntry = mediaDataRepository.mediaEntries.value[key]
- val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
- val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
- val resumeAction: Runnable? = currentEntry?.resumeAction
- val hasCheckedForResume = currentEntry?.hasCheckedForResume == true
- val active = currentEntry?.active ?: true
+ val mediaController = mediaControllerFactory.create(result.token!!)
+ val oldEntry = mediaDataRepository.mediaEntries.value[key]
+ val instanceId = oldEntry?.instanceId ?: logger.getNewInstanceId()
+ val createdTimestampMillis = oldEntry?.createdTimestampMillis ?: 0L
+ val resumeAction: Runnable? = oldEntry?.resumeAction
+ val hasCheckedForResume = oldEntry?.hasCheckedForResume == true
+ val active = oldEntry?.active ?: true
+
+ val mediaData =
+ MediaData(
+ userId = sbn.normalizedUserId,
+ initialized = true,
+ app = result.appName,
+ appIcon = result.appIcon,
+ artist = result.artist,
+ song = result.song,
+ artwork = result.artworkIcon,
+ actions = result.actionIcons,
+ actionsToShowInCompact = result.actionsToShowInCompact,
+ semanticActions = result.semanticActions,
+ packageName = sbn.packageName,
+ token = result.token,
+ clickIntent = result.clickIntent,
+ device = result.device,
+ active = active,
+ resumeAction = resumeAction,
+ playbackLocation = result.playbackLocation,
+ notificationKey = key,
+ hasCheckedForResume = hasCheckedForResume,
+ isPlaying = result.isPlaying,
+ isClearable = !sbn.isOngoing,
+ lastActive = lastActive,
+ createdTimestampMillis = createdTimestampMillis,
+ instanceId = instanceId,
+ appUid = result.appUid,
+ isExplicit = result.isExplicit,
+ )
+
+ if (isSameMediaData(context, mediaController, mediaData, oldEntry)) {
+ mediaLogger.logDuplicateMediaNotification(key)
+ return@withContext
+ }
// We need to log the correct media added.
if (isNewlyActiveEntry) {
@@ -849,7 +889,7 @@
instanceId,
result.playbackLocation
)
- } else if (result.playbackLocation != currentEntry?.playbackLocation) {
+ } else if (result.playbackLocation != oldEntry?.playbackLocation) {
logger.logPlaybackLocationChange(
result.appUid,
sbn.packageName,
@@ -858,40 +898,7 @@
)
}
- withContext(mainDispatcher) {
- onMediaDataLoaded(
- key,
- oldKey,
- MediaData(
- userId = sbn.normalizedUserId,
- initialized = true,
- app = result.appName,
- appIcon = result.appIcon,
- artist = result.artist,
- song = result.song,
- artwork = result.artworkIcon,
- actions = result.actionIcons,
- actionsToShowInCompact = result.actionsToShowInCompact,
- semanticActions = result.semanticActions,
- packageName = sbn.packageName,
- token = result.token,
- clickIntent = result.clickIntent,
- device = result.device,
- active = active,
- resumeAction = resumeAction,
- playbackLocation = result.playbackLocation,
- notificationKey = key,
- hasCheckedForResume = hasCheckedForResume,
- isPlaying = result.isPlaying,
- isClearable = !sbn.isOngoing,
- lastActive = lastActive,
- createdTimestampMillis = createdTimestampMillis,
- instanceId = instanceId,
- appUid = result.appUid,
- isExplicit = result.isExplicit,
- )
- )
- }
+ withContext(mainDispatcher) { onMediaDataLoaded(key, oldKey, mediaData) }
}
@Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
@@ -1002,13 +1009,13 @@
}
// Control buttons
- // If flag is enabled and controller has a PlaybackState, create actions from session info
+ // If controller has a PlaybackState, create actions from session info
// Otherwise, use the notification actions
- var actionIcons: List<MediaAction> = emptyList()
+ var actionIcons: List<MediaNotificationAction> = emptyList()
var actionsToShowCollapsed: List<Int> = emptyList()
val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
if (semanticActions == null) {
- val actions = createActionsFromNotification(context, activityStarter, sbn)
+ val actions = createActionsFromNotification(context, sbn)
actionIcons = actions.first
actionsToShowCollapsed = actions.second
}
@@ -1023,57 +1030,72 @@
else MediaData.PLAYBACK_CAST_LOCAL
val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) }
- val currentEntry = mediaDataRepository.mediaEntries.value.get(key)
- val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
+ val oldEntry = mediaDataRepository.mediaEntries.value.get(key)
+ val instanceId = oldEntry?.instanceId ?: logger.getNewInstanceId()
val appUid = appInfo?.uid ?: Process.INVALID_UID
+ val lastActive = systemClock.elapsedRealtime()
+ val createdTimestampMillis = oldEntry?.createdTimestampMillis ?: 0L
+ val resumeAction: Runnable? = mediaDataRepository.mediaEntries.value[key]?.resumeAction
+ val hasCheckedForResume =
+ mediaDataRepository.mediaEntries.value[key]?.hasCheckedForResume == true
+ val active = mediaDataRepository.mediaEntries.value[key]?.active ?: true
+ var mediaData =
+ MediaData(
+ sbn.normalizedUserId,
+ true,
+ appName,
+ smallIcon,
+ artist,
+ song,
+ artWorkIcon,
+ actionIcons,
+ actionsToShowCollapsed,
+ semanticActions,
+ sbn.packageName,
+ token,
+ notif.contentIntent,
+ device,
+ active,
+ resumeAction = resumeAction,
+ playbackLocation = playbackLocation,
+ notificationKey = key,
+ hasCheckedForResume = hasCheckedForResume,
+ isPlaying = isPlaying,
+ isClearable = !sbn.isOngoing,
+ lastActive = lastActive,
+ createdTimestampMillis = createdTimestampMillis,
+ instanceId = instanceId,
+ appUid = appUid,
+ isExplicit = isExplicit,
+ smartspaceId = SmallHash.hash(appUid + systemClock.currentTimeMillis().toInt()),
+ )
+
+ if (isSameMediaData(context, mediaController, mediaData, oldEntry)) {
+ mediaLogger.logDuplicateMediaNotification(key)
+ return
+ }
+
if (isNewlyActiveEntry) {
logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId)
logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
- } else if (playbackLocation != currentEntry?.playbackLocation) {
+ } else if (playbackLocation != oldEntry?.playbackLocation) {
logger.logPlaybackLocationChange(appUid, sbn.packageName, instanceId, playbackLocation)
}
- val lastActive = systemClock.elapsedRealtime()
- val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
foregroundExecutor.execute {
- val resumeAction: Runnable? = mediaDataRepository.mediaEntries.value[key]?.resumeAction
- val hasCheckedForResume =
+ val oldResumeAction: Runnable? =
+ mediaDataRepository.mediaEntries.value[key]?.resumeAction
+ val oldHasCheckedForResume =
mediaDataRepository.mediaEntries.value[key]?.hasCheckedForResume == true
- val active = mediaDataRepository.mediaEntries.value[key]?.active ?: true
- onMediaDataLoaded(
- key,
- oldKey,
- MediaData(
- sbn.normalizedUserId,
- true,
- appName,
- smallIcon,
- artist,
- song,
- artWorkIcon,
- actionIcons,
- actionsToShowCollapsed,
- semanticActions,
- sbn.packageName,
- token,
- notif.contentIntent,
- device,
- active,
- resumeAction = resumeAction,
- playbackLocation = playbackLocation,
- notificationKey = key,
- hasCheckedForResume = hasCheckedForResume,
- isPlaying = isPlaying,
- isClearable = !sbn.isOngoing,
- lastActive = lastActive,
- createdTimestampMillis = createdTimestampMillis,
- instanceId = instanceId,
- appUid = appUid,
- isExplicit = isExplicit,
- smartspaceId = SmallHash.hash(appUid + systemClock.currentTimeMillis().toInt()),
+ val oldActive = mediaDataRepository.mediaEntries.value[key]?.active ?: true
+ mediaData =
+ mediaData.copy(
+ resumeAction = oldResumeAction,
+ hasCheckedForResume = oldHasCheckedForResume,
+ active = oldActive
)
- )
+ onMediaDataLoaded(key, oldKey, mediaData)
}
}
@@ -1229,7 +1251,7 @@
.loadDrawable(context),
action,
context.getString(R.string.controls_media_resume),
- MediaControlDrawables.getPlayBackground(context)
+ context.getDrawable(R.drawable.ic_media_play_container)
)
}
@@ -1403,7 +1425,7 @@
val updated =
data.copy(
token = null,
- actions = actions,
+ actions = listOf(),
semanticActions = MediaButton(playOrPause = resumeAction),
actionsToShowInCompact = listOf(0),
active = false,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
new file mode 100644
index 0000000..55d7b1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
@@ -0,0 +1,179 @@
+/*
+ * 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.annotation.WorkerThread
+import android.app.PendingIntent
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.media.session.MediaController
+import android.media.session.PlaybackState
+import android.util.Log
+import com.android.systemui.Flags.mediaControlsPostsOptimization
+import com.android.systemui.biometrics.Utils.toBitmap
+import com.android.systemui.media.controls.shared.model.MediaData
+
+private const val TAG = "MediaProcessingHelper"
+
+/**
+ * Compares [new] media data to [old] media data.
+ *
+ * @param context Context
+ * @param newController media controller of the new media data.
+ * @param new new media data.
+ * @param old old media data.
+ * @return whether new and old contain same data
+ */
+fun isSameMediaData(
+ context: Context,
+ newController: MediaController,
+ new: MediaData,
+ old: MediaData?
+): Boolean {
+ if (old == null || !mediaControlsPostsOptimization()) return false
+
+ return new.userId == old.userId &&
+ new.app == old.app &&
+ new.artist == old.artist &&
+ new.song == old.song &&
+ new.packageName == old.packageName &&
+ new.isExplicit == old.isExplicit &&
+ new.appUid == old.appUid &&
+ new.notificationKey == old.notificationKey &&
+ new.isPlaying == old.isPlaying &&
+ new.isClearable == old.isClearable &&
+ new.playbackLocation == old.playbackLocation &&
+ new.device == old.device &&
+ new.initialized == old.initialized &&
+ new.resumption == old.resumption &&
+ new.token == old.token &&
+ new.resumeProgress == old.resumeProgress &&
+ areClickIntentsEqual(new.clickIntent, old.clickIntent) &&
+ areActionsEqual(context, newController, new, old) &&
+ areIconsEqual(context, new.artwork, old.artwork) &&
+ areIconsEqual(context, new.appIcon, old.appIcon)
+}
+
+/** Returns whether actions lists are equal. */
+fun areCustomActionListsEqual(
+ first: List<PlaybackState.CustomAction>?,
+ second: List<PlaybackState.CustomAction>?
+): Boolean {
+ // Same object, or both null
+ if (first === second) {
+ return true
+ }
+
+ // Only one null, or different number of actions
+ if ((first == null || second == null) || (first.size != second.size)) {
+ return false
+ }
+
+ // Compare individual actions
+ first.asSequence().zip(second.asSequence()).forEach { (firstAction, secondAction) ->
+ if (!areCustomActionsEqual(firstAction, secondAction)) {
+ return false
+ }
+ }
+ return true
+}
+
+private fun areCustomActionsEqual(
+ firstAction: PlaybackState.CustomAction,
+ secondAction: PlaybackState.CustomAction
+): Boolean {
+ if (
+ firstAction.action != secondAction.action ||
+ firstAction.name != secondAction.name ||
+ firstAction.icon != secondAction.icon
+ ) {
+ return false
+ }
+
+ if ((firstAction.extras == null) != (secondAction.extras == null)) {
+ return false
+ }
+ if (firstAction.extras != null) {
+ firstAction.extras.keySet().forEach { key ->
+ if (firstAction.extras[key] != secondAction.extras[key]) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+@WorkerThread
+private fun areIconsEqual(context: Context, new: Icon?, old: Icon?): Boolean {
+ if (new == old) return true
+ if (new == null || old == null || new.type != old.type) return false
+ return if (new.type == Icon.TYPE_BITMAP || new.type == Icon.TYPE_ADAPTIVE_BITMAP) {
+ if (new.bitmap.isRecycled || old.bitmap.isRecycled) {
+ Log.e(TAG, "Cannot compare recycled bitmap")
+ return false
+ }
+ new.bitmap.sameAs(old.bitmap)
+ } else {
+ val newDrawable = new.loadDrawable(context)
+ val oldDrawable = old.loadDrawable(context)
+
+ return newDrawable?.toBitmap()?.sameAs(oldDrawable?.toBitmap()) ?: false
+ }
+}
+
+private fun areActionsEqual(
+ context: Context,
+ newController: MediaController,
+ new: MediaData,
+ old: MediaData
+): Boolean {
+ val oldState = MediaController(context, old.token!!).playbackState
+ return if (
+ new.semanticActions == null &&
+ old.semanticActions == null &&
+ new.actions.size == old.actions.size
+ ) {
+ var same = true
+ new.actions.asSequence().zip(old.actions.asSequence()).forEach {
+ if (
+ it.first.actionIntent?.intent?.filterEquals(it.second.actionIntent?.intent) !=
+ true ||
+ it.first.icon != it.second.icon ||
+ it.first.contentDescription != it.second.contentDescription
+ ) {
+ same = false
+ return@forEach
+ }
+ }
+ same
+ } else if (new.semanticActions != null && old.semanticActions != null) {
+ oldState?.actions == newController.playbackState?.actions &&
+ areCustomActionListsEqual(
+ oldState?.customActions,
+ newController.playbackState?.customActions
+ )
+ } else {
+ false
+ }
+}
+
+private fun areClickIntentsEqual(newIntent: PendingIntent?, oldIntent: PendingIntent?): Boolean {
+ if ((newIntent == null && oldIntent == null) || newIntent === oldIntent) return true
+ if (newIntent == null || oldIntent == null) return false
+
+ return newIntent.intent?.filterEquals(oldIntent.intent) == true
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
index fc31903..275f1ee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
@@ -16,12 +16,14 @@
package com.android.systemui.media.controls.domain.pipeline
+import android.annotation.WorkerThread
import android.media.session.MediaController
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.SystemProperties
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
@@ -32,6 +34,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
+import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@@ -49,6 +52,8 @@
@Inject
constructor(
private val mediaControllerFactory: MediaControllerFactory,
+ @Background private val bgExecutor: Executor,
+ @Main private val uiExecutor: Executor,
@Main private val mainExecutor: DelayableExecutor,
private val logger: MediaTimeoutLogger,
statusBarStateController: SysuiStatusBarStateController,
@@ -147,19 +152,21 @@
}
reusedListener?.let {
- val wasPlaying = it.isPlaying()
- logger.logUpdateListener(key, wasPlaying)
- it.setMediaData(data)
- it.key = key
- mediaListeners[key] = it
- if (wasPlaying != it.isPlaying()) {
- // If a player becomes active because of a migration, we'll need to broadcast
- // its state. Doing it now would lead to reentrant callbacks, so let's wait
- // until we're done.
- mainExecutor.execute {
- if (mediaListeners[key]?.isPlaying() == true) {
- logger.logDelayedUpdate(key)
- timeoutCallback.invoke(key, false /* timedOut */)
+ bgExecutor.execute {
+ val wasPlaying = it.isPlaying()
+ logger.logUpdateListener(key, wasPlaying)
+ it.setMediaData(data)
+ it.key = key
+ mediaListeners[key] = it
+ if (wasPlaying != it.isPlaying()) {
+ // If a player becomes active because of a migration, we'll need to broadcast
+ // its state. Doing it now would lead to reentrant callbacks, so let's wait
+ // until we're done.
+ mainExecutor.execute {
+ if (mediaListeners[key]?.isPlaying() == true) {
+ logger.logDelayedUpdate(key)
+ timeoutCallback.invoke(key, false /* timedOut */)
+ }
}
}
}
@@ -217,18 +224,20 @@
private set
fun Int.isPlaying() = isPlayingState(this)
+
fun isPlaying() = lastState?.state?.isPlaying() ?: false
init {
- setMediaData(data)
+ bgExecutor.execute { setMediaData(data) }
}
fun destroy() {
- mediaController?.unregisterCallback(this)
+ bgExecutor.execute { mediaController?.unregisterCallback(this) }
cancellation?.run()
destroyed = true
}
+ @WorkerThread
fun setMediaData(data: MediaData) {
sessionToken = data.token
destroyed = false
@@ -258,7 +267,7 @@
if (resumption == true) {
// Some apps create a session when MBS is queried. We should unregister the
// controller since it will no longer be valid, but don't cancel the timeout
- mediaController?.unregisterCallback(this)
+ bgExecutor.execute { mediaController?.unregisterCallback(this) }
} else {
// For active controls, if the session is destroyed, clean up everything since we
// will need to recreate it if this key is updated later
@@ -284,7 +293,7 @@
if ((!actionsSame || !playingStateSame) && state != null && dispatchEvents) {
logger.logStateCallback(key)
- stateCallback.invoke(key, state)
+ uiExecutor.execute { stateCallback.invoke(key, state) }
}
if (playingStateSame && !resumptionChanged) {
@@ -313,7 +322,7 @@
expireMediaTimeout(key, "playback started - $state, $key")
timedOut = false
if (dispatchEvents) {
- timeoutCallback(key, timedOut)
+ uiExecutor.execute { timeoutCallback(key, timedOut) }
}
}
}
@@ -337,60 +346,13 @@
}
}
- private fun areCustomActionListsEqual(
- first: List<PlaybackState.CustomAction>?,
- second: List<PlaybackState.CustomAction>?
- ): Boolean {
- // Same object, or both null
- if (first === second) {
- return true
- }
-
- // Only one null, or different number of actions
- if ((first == null || second == null) || (first.size != second.size)) {
- return false
- }
-
- // Compare individual actions
- first.asSequence().zip(second.asSequence()).forEach { (firstAction, secondAction) ->
- if (!areCustomActionsEqual(firstAction, secondAction)) {
- return false
- }
- }
- return true
- }
-
- private fun areCustomActionsEqual(
- firstAction: PlaybackState.CustomAction,
- secondAction: PlaybackState.CustomAction
- ): Boolean {
- if (
- firstAction.action != secondAction.action ||
- firstAction.name != secondAction.name ||
- firstAction.icon != secondAction.icon
- ) {
- return false
- }
-
- if ((firstAction.extras == null) != (secondAction.extras == null)) {
- return false
- }
- if (firstAction.extras != null) {
- firstAction.extras.keySet().forEach { key ->
- if (firstAction.extras.get(key) != secondAction.extras.get(key)) {
- return false
- }
- }
- }
- return true
- }
-
/** Listens to changes in recommendation card data and schedules a timeout for its expiration */
private inner class RecommendationListener(var key: String, data: SmartspaceMediaData) {
private var timedOut = false
var destroyed = false
var expiration = Long.MAX_VALUE
private set
+
var cancellation: Runnable? = null
private set
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 245f6f8..130868d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -32,6 +32,7 @@
import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
+import com.android.systemui.media.controls.domain.pipeline.getNotificationActions
import com.android.systemui.media.controls.shared.model.MediaControlModel
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.MediaSmartspaceLogger
@@ -102,7 +103,7 @@
artwork = artwork,
deviceData = device,
semanticActionButtons = semanticActions,
- notificationActionButtons = actions,
+ notificationActionButtons = getNotificationActions(data.actions, activityStarter),
actionsToShowInCollapsed = actionsToShowInCompact,
isDismissible = isClearable,
isResume = resumption,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
index e4047e5..9ee59d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
@@ -80,6 +80,7 @@
field?.disconnect()
field = value
}
+
private var currentUserId: Int = context.userId
@VisibleForTesting
@@ -89,7 +90,7 @@
if (Intent.ACTION_USER_UNLOCKED == intent.action) {
val userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
if (userId == currentUserId) {
- loadMediaResumptionControls()
+ backgroundExecutor.execute { loadMediaResumptionControls() }
}
}
}
@@ -254,15 +255,15 @@
if (data.resumeAction == null && !data.hasCheckedForResume && isEligibleForResume) {
// TODO also check for a media button receiver intended for restarting (b/154127084)
// Set null action to prevent additional attempts to connect
- mediaDataManager.setResumeAction(key, null)
- Log.d(TAG, "Checking for service component for " + data.packageName)
- val pm = context.packageManager
- val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE)
- val resumeInfo = pm.queryIntentServicesAsUser(serviceIntent, 0, currentUserId)
+ backgroundExecutor.execute {
+ mediaDataManager.setResumeAction(key, null)
+ Log.d(TAG, "Checking for service component for " + data.packageName)
+ val pm = context.packageManager
+ val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE)
+ val resumeInfo = pm.queryIntentServicesAsUser(serviceIntent, 0, currentUserId)
- val inf = resumeInfo?.filter { it.serviceInfo.packageName == data.packageName }
- if (inf != null && inf.size > 0) {
- backgroundExecutor.execute {
+ val inf = resumeInfo?.filter { it.serviceInfo.packageName == data.packageName }
+ if (inf != null && inf.size > 0) {
tryUpdateResumptionList(key, inf!!.get(0).componentInfo.componentName)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
index c78220e..95ca11c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
@@ -17,20 +17,12 @@
package com.android.systemui.media.controls.shared
import android.content.Context
-import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
import com.android.systemui.Flags.mediaControlsDrawablesReuse
import com.android.systemui.res.R
object MediaControlDrawables {
- // Play/Pause Button drawables.
- private var progress: Drawable? = null
- private var connecting: Drawable? = null
- private var playIcon: AnimatedVectorDrawable? = null
- private var playBackground: AnimatedVectorDrawable? = null
- private var pauseIcon: AnimatedVectorDrawable? = null
- private var pauseBackground: AnimatedVectorDrawable? = null
// Prev button.
private var prevIcon: Drawable? = null
// Next button.
@@ -40,81 +32,6 @@
private var antenna: Drawable? = null
private var groupDevice: Drawable? = null
private var homeDevices: Drawable? = null
- // Guts drawables.
- private var outline: Drawable? = null
- private var solid: Drawable? = null
-
- fun getProgress(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuse()) {
- return context.getDrawable(com.android.internal.R.drawable.progress_small_material)
- }
- return progress?.mutate()
- ?: context.getDrawable(com.android.internal.R.drawable.progress_small_material).also {
- progress = it
- }
- }
-
- fun getConnecting(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuse()) {
- return context.getDrawable(R.drawable.ic_media_connecting_container)
- }
- return connecting?.mutate()
- ?: context.getDrawable(R.drawable.ic_media_connecting_container).also {
- connecting = it
- }
- }
-
- fun getPlayIcon(context: Context): AnimatedVectorDrawable? {
- if (!mediaControlsDrawablesReuse()) {
- return context.getDrawable(R.drawable.ic_media_play) as AnimatedVectorDrawable?
- }
- return playIcon?.let {
- it.reset()
- it.mutate() as AnimatedVectorDrawable
- }
- ?: (context.getDrawable(R.drawable.ic_media_play) as AnimatedVectorDrawable?).also {
- playIcon = it
- }
- }
-
- fun getPlayBackground(context: Context): AnimatedVectorDrawable? {
- if (!mediaControlsDrawablesReuse()) {
- return context.getDrawable(R.drawable.ic_media_play_container)
- as AnimatedVectorDrawable?
- }
- return playBackground?.let {
- it.reset()
- it.mutate() as AnimatedVectorDrawable
- }
- ?: (context.getDrawable(R.drawable.ic_media_play_container) as AnimatedVectorDrawable?)
- .also { playBackground = it }
- }
-
- fun getPauseIcon(context: Context): AnimatedVectorDrawable? {
- if (!mediaControlsDrawablesReuse()) {
- return context.getDrawable(R.drawable.ic_media_pause) as AnimatedVectorDrawable?
- }
- return pauseIcon?.let {
- it.reset()
- it.mutate() as AnimatedVectorDrawable
- }
- ?: (context.getDrawable(R.drawable.ic_media_pause) as AnimatedVectorDrawable?).also {
- pauseIcon = it
- }
- }
-
- fun getPauseBackground(context: Context): AnimatedVectorDrawable? {
- if (!mediaControlsDrawablesReuse()) {
- return context.getDrawable(R.drawable.ic_media_pause_container)
- as AnimatedVectorDrawable?
- }
- return pauseBackground?.let {
- it.reset()
- it.mutate() as AnimatedVectorDrawable
- }
- ?: (context.getDrawable(R.drawable.ic_media_pause_container) as AnimatedVectorDrawable?)
- .also { pauseBackground = it }
- }
fun getNextIcon(context: Context): Drawable? {
if (!mediaControlsDrawablesReuse()) {
@@ -165,19 +82,4 @@
return homeDevices
?: context.getDrawable(R.drawable.ic_media_home_devices).also { homeDevices = it }
}
-
- fun getOutline(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuse()) {
- return context.getDrawable(R.drawable.qs_media_outline_button)
- }
- return outline
- ?: context.getDrawable(R.drawable.qs_media_outline_button).also { outline = it }
- }
-
- fun getSolid(context: Context): Drawable? {
- if (!mediaControlsDrawablesReuse()) {
- return context.getDrawable(R.drawable.qs_media_solid_button)
- }
- return solid ?: context.getDrawable(R.drawable.qs_media_solid_button).also { solid = it }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
index 2b710b5..7d20e17 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
@@ -114,6 +114,15 @@
)
}
+ fun logDuplicateMediaNotification(key: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = key },
+ { "duplicate media notification $str1 posted" }
+ )
+ }
+
companion object {
private const val TAG = "MediaLog"
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
index 40b3477..aed8609 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
@@ -39,7 +39,7 @@
/** Album artwork. */
val artwork: Icon? = null,
/** List of generic action buttons for the media player, based on notification actions */
- val actions: List<MediaAction> = emptyList(),
+ val actions: List<MediaNotificationAction> = emptyList(),
/** Same as above, but shown on smaller versions of the player, like in QQS or keyguard. */
val actionsToShowInCompact: List<Int> = emptyList(),
/**
@@ -162,6 +162,14 @@
val rebindId: Int? = null
)
+/** State of a media action from notification. */
+data class MediaNotificationAction(
+ val isAuthenticationRequired: Boolean,
+ val actionIntent: PendingIntent?,
+ val icon: Drawable?,
+ val contentDescription: CharSequence?
+)
+
/** State of the media device. */
data class MediaDeviceData
@JvmOverloads
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 87610cf..8bec46a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -21,6 +21,7 @@
import static com.android.settingslib.flags.Flags.legacyLeAudioSharing;
import static com.android.systemui.Flags.communalHub;
import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
+import static com.android.systemui.media.controls.domain.pipeline.MediaActionsKt.getNotificationActions;
import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
import android.animation.Animator;
@@ -1170,7 +1171,7 @@
// Set all the generic buttons
List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
- List<MediaAction> actions = data.getActions();
+ List<MediaAction> actions = getNotificationActions(data.getActions(), mActivityStarter);
int i = 0;
for (; i < actions.size() && i < genericButtons.size(); i++) {
boolean showInCompact = actionsWhenCollapsed.contains(i);
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index f460134..64820e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -30,7 +30,6 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaControlInteractor
-import com.android.systemui.media.controls.shared.MediaControlDrawables
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaControlModel
@@ -285,9 +284,9 @@
},
cancelTextBackground =
if (model.isDismissible) {
- MediaControlDrawables.getOutline(applicationContext)
+ applicationContext.getDrawable(R.drawable.qs_media_outline_button)
} else {
- MediaControlDrawables.getSolid(applicationContext)
+ applicationContext.getDrawable(R.drawable.qs_media_solid_button)
},
onSettingsClicked = {
logger.logLongPressSettings(model.uid, model.packageName, model.instanceId)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index cef1e69..2a9fe83 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -32,6 +32,7 @@
import androidx.core.view.GestureDetectorCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
+import com.android.systemui.Flags
import com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.FalsingManager
@@ -102,9 +103,11 @@
}
_progress.postValue(value)
}
+
private val _progress = MutableLiveData<Progress>().apply { postValue(_data) }
val progress: LiveData<Progress>
get() = _progress
+
private var controller: MediaController? = null
set(value) {
if (field?.sessionToken != value?.sessionToken) {
@@ -113,6 +116,7 @@
field = value
}
}
+
private var playbackState: PlaybackState? = null
private var callback =
object : MediaController.Callback() {
@@ -128,6 +132,15 @@
override fun onSessionDestroyed() {
clearController()
}
+
+ override fun onMetadataChanged(metadata: MediaMetadata?) {
+ if (!Flags.mediaControlsPostsOptimization()) return
+
+ val (enabled, duration) = getEnabledStateAndDuration(metadata)
+ if (_data.duration != duration) {
+ _data = _data.copy(enabled = enabled, duration = duration)
+ }
+ }
}
private var cancel: Runnable? = null
@@ -233,22 +246,13 @@
fun updateController(mediaController: MediaController?) {
controller = mediaController
playbackState = controller?.playbackState
- val mediaMetadata = controller?.metadata
+ val (enabled, duration) = getEnabledStateAndDuration(controller?.metadata)
val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
val position = playbackState?.position?.toInt()
- val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
val playing =
NotificationMediaManager.isPlayingState(
playbackState?.state ?: PlaybackState.STATE_NONE
)
- val enabled =
- if (
- playbackState == null ||
- playbackState?.getState() == PlaybackState.STATE_NONE ||
- (duration <= 0)
- )
- false
- else true
_data = Progress(enabled, seekAvailable, playing, scrubbing, position, duration, listening)
checkIfPollingNeeded()
}
@@ -368,6 +372,16 @@
}
}
+ /** returns a pair of whether seekbar is enabled and the duration of media. */
+ private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
+ val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
+ val enabled =
+ !(playbackState == null ||
+ playbackState?.state == PlaybackState.STATE_NONE ||
+ (duration <= 0))
+ return Pair(enabled, duration)
+ }
+
/**
* This method specifies if user made a bad seekbar grab or not. If the vertical distance from
* first touch on seekbar is more than the horizontal distance, this means that the seekbar grab
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index a65243d..d4af1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -29,9 +29,8 @@
* Check whether media control actions should be based on PlaybackState instead of notification
*/
fun areMediaSessionActionsEnabled(packageName: String, user: UserHandle): Boolean {
- val enabled = StatusBarManager.useMediaSessionActionsForApp(packageName, user)
// Allow global override with flag
- return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
+ return StatusBarManager.useMediaSessionActionsForApp(packageName, user)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
index 1502df7..078d534 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
@@ -19,6 +19,7 @@
import android.app.StatusBarManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
import com.android.systemui.temporarydisplay.TemporaryViewLogger
import javax.inject.Inject
@@ -50,6 +51,15 @@
MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
}
+ fun logRippleAnimationEnd(id: Int) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ { int1 = id },
+ { "ripple animation for view with id: $int1 is ended" }
+ )
+ }
+
companion object {
private const val TAG = "MediaTttReceiver"
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
index fbd7fd3..a232971 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
@@ -32,6 +32,7 @@
constructor(
private val context: Context,
private val windowManager: WindowManager,
+ private val mediaTttReceiverLogger: MediaTttReceiverLogger,
) {
private var maxRippleWidth: Float = 0f
@@ -90,12 +91,12 @@
/** Expands the ripple to cover the screen. */
fun expandToSuccessState(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable?) {
layoutRipple(rippleView, isFullScreen = true)
- rippleView.expandToFull(maxRippleHeight, onAnimationEnd)
+ rippleView.expandToFull(maxRippleHeight, mediaTttReceiverLogger, onAnimationEnd)
}
/** Collapses the ripple. */
fun collapseRipple(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable? = null) {
- rippleView.collapseRipple(onAnimationEnd)
+ rippleView.collapseRipple(mediaTttReceiverLogger, onAnimationEnd)
}
private fun layoutRipple(rippleView: ReceiverChipRippleView, isFullScreen: Boolean = false) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index 35018f1..81059e3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -24,9 +24,7 @@
import com.android.systemui.surfaceeffects.ripple.RippleView
import kotlin.math.pow
-/**
- * An expanding ripple effect for the media tap-to-transfer receiver chip.
- */
+/** An expanding ripple effect for the media tap-to-transfer receiver chip. */
class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
// Indicates whether the ripple started expanding.
@@ -46,24 +44,34 @@
}
/** Used to animate out the ripple. No-op if the ripple was never started via [startRipple]. */
- fun collapseRipple(onAnimationEnd: Runnable? = null) {
+ fun collapseRipple(logger: MediaTttReceiverLogger, onAnimationEnd: Runnable? = null) {
if (!isStarted) {
return // Ignore if ripple is not started yet.
}
duration = DEFAULT_DURATION
// Reset all listeners to animator.
animator.removeAllListeners()
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- onAnimationEnd?.run()
- isStarted = false
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ animation?.let {
+ visibility = GONE
+ logger.logRippleAnimationEnd(id)
+ }
+ onAnimationEnd?.run()
+ isStarted = false
+ }
}
- })
+ )
animator.reverse()
}
// Expands the ripple to cover full screen.
- fun expandToFull(newHeight: Float, onAnimationEnd: Runnable? = null) {
+ fun expandToFull(
+ newHeight: Float,
+ logger: MediaTttReceiverLogger,
+ onAnimationEnd: Runnable? = null
+ ) {
if (!isStarted) {
return
}
@@ -85,13 +93,18 @@
rippleShader.time = now.toFloat()
invalidate()
}
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- animation?.let { visibility = GONE }
- onAnimationEnd?.run()
- isStarted = false
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ animation?.let {
+ visibility = GONE
+ logger.logRippleAnimationEnd(id)
+ }
+ onAnimationEnd?.run()
+ isStarted = false
+ }
}
- })
+ )
animator.start()
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 9b1ca1e..6440205 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -122,8 +122,10 @@
@Provides
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope
- fun bindConfigurationController(context: Context): ConfigurationController =
- ConfigurationControllerImpl(context)
+ fun bindConfigurationController(
+ context: Context,
+ configurationControlleFactory: ConfigurationControllerImpl.Factory
+ ): ConfigurationController = configurationControlleFactory.create(context)
@Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 18c6f53..4251b81 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -307,7 +307,7 @@
private void setUpDialog(AlertDialog dialog) {
SystemUIDialog.registerDismissListener(dialog);
- SystemUIDialog.applyFlags(dialog);
+ SystemUIDialog.applyFlags(dialog, /* showWhenLocked= */ false);
SystemUIDialog.setDialogSize(dialog);
dialog.setOnCancelListener(this::onDialogDismissedOrCancelled);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index e44069f..b3c697e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -425,6 +425,18 @@
}
}
+ private void appTransitionPending(boolean pending) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().appTransitionPending(pending);
+ } catch (RemoteException e) {
+ Log.e(TAG, "appTransitionPending() failed, pending: " + pending, e);
+ }
+ }
+
@Override
public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
@BackDispositionMode int backDisposition, boolean showImeSwitcher) {
@@ -533,6 +545,21 @@
}
}
+ @Override
+ public void appTransitionPending(int displayId, boolean forced) {
+ appTransitionPending(true);
+ }
+
+ @Override
+ public void appTransitionCancelled(int displayId) {
+ appTransitionPending(false);
+ }
+
+ @Override
+ public void appTransitionFinished(int displayId) {
+ appTransitionPending(false);
+ }
+
private void clearTransient() {
if (mTaskbarTransientShowing) {
mTaskbarTransientShowing = false;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 6bd880d..89d76f0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -29,6 +29,7 @@
import static java.util.stream.Collectors.joining;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -1331,21 +1332,20 @@
}
}
- public void setBackAnimation(BackAnimation backAnimation) {
+ public void setBackAnimation(@Nullable BackAnimation backAnimation) {
mBackAnimation = backAnimation;
- mBackAnimation.setPilferPointerCallback(() -> {
- pilferPointers();
- });
- mBackAnimation.setTopUiRequestCallback(
- (requestTopUi, tag) -> mUiThreadContext.getExecutor().execute(() ->
- mNotificationShadeWindowController.setRequestTopUi(requestTopUi, tag)));
- updateBackAnimationThresholds();
- if (mLightBarControllerProvider.get() != null) {
- mBackAnimation.setStatusBarCustomizer((appearance) -> {
- mUiThreadContext.getExecutor().execute(() ->
- mLightBarControllerProvider.get()
- .customizeStatusBarAppearance(appearance));
- });
+ if (backAnimation != null) {
+ backAnimation.setPilferPointerCallback(this::pilferPointers);
+ backAnimation.setTopUiRequestCallback(
+ (requestTopUi, tag) -> mUiThreadContext.getExecutor().execute(() ->
+ mNotificationShadeWindowController.setRequestTopUi(requestTopUi, tag)));
+ updateBackAnimationThresholds();
+ if (mLightBarControllerProvider.get() != null) {
+ mBackAnimation.setStatusBarCustomizer((appearance) ->
+ mUiThreadContext.getExecutor().execute(() ->
+ mLightBarControllerProvider.get()
+ .customizeStatusBarAppearance(appearance)));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index c706c3e..c70a523 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -34,7 +34,6 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
-import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
@@ -60,7 +59,6 @@
import android.app.IActivityTaskManager;
import android.app.StatusBarManager;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -105,7 +103,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
-import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -140,7 +137,6 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.rotation.RotationPolicyUtil;
@@ -166,6 +162,7 @@
import com.android.systemui.util.ViewController;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import dagger.Lazy;
@@ -1625,11 +1622,9 @@
}
private boolean onAccessibilityLongClick(View v) {
- final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
- intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
- mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
+ final Display display = v.getDisplay();
+ mAccessibilityManager.notifyAccessibilityButtonLongClicked(
+ display != null ? display.getDisplayId() : mDisplayTracker.getDefaultDisplayId());
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
new file mode 100644
index 0000000..b6868c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.notifications.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** Models the UI state for the user actions for navigating to other scenes or overlays. */
+class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() :
+ UserActionsViewModel() {
+
+ override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+ setActions(
+ mapOf(
+ Swipe.Up to UserActionResult.HideOverlay(Overlays.NotificationsShade),
+ Back to UserActionResult.HideOverlay(Overlays.NotificationsShade),
+ )
+ )
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): NotificationsShadeOverlayActionsViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
new file mode 100644
index 0000000..5be225c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.notifications.ui.viewmodel
+
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Models UI state used to render the content of the notifications shade overlay.
+ *
+ * Different from [NotificationsShadeOverlayActionsViewModel], which only models user actions that
+ * can be performed to navigate to other scenes.
+ */
+class NotificationsShadeOverlayContentViewModel
+@AssistedInject
+constructor(
+ val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
+ val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
+ private val sceneInteractor: SceneInteractor,
+) {
+ fun onScrimClicked() {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = "Shade scrim clicked",
+ )
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): NotificationsShadeOverlayContentViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
similarity index 66%
rename from packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
index 9fb09c0..11854d9 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
@@ -18,39 +18,36 @@
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeAlignment
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
/**
* Models the UI state for the user actions that the user can perform to navigate to other scenes.
*/
-class NotificationsShadeSceneActionsViewModel
-@AssistedInject
-constructor(
- private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
+class NotificationsShadeUserActionsViewModel @AssistedInject constructor() :
+ UserActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
setActions(
mapOf(
- if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
- Swipe.Up
- } else {
- Swipe.Down
- } to SceneFamilies.Home,
Back to SceneFamilies.Home,
+ Swipe.Up to SceneFamilies.Home,
+ Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
+ ReplaceByOverlay(Overlays.QuickSettingsShade),
)
)
}
@AssistedFactory
interface Factory {
- fun create(): NotificationsShadeSceneActionsViewModel
+ fun create(): NotificationsShadeUserActionsViewModel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
index 3a8fe71..ef1f834 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
@@ -19,6 +19,7 @@
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.QRCodeScannerTile
import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
@@ -51,7 +52,7 @@
@IntoMap
@StringKey(QR_CODE_SCANNER_TILE_SPEC)
fun provideQrCodeScannerAvailabilityInteractor(
- impl: QRCodeScannerTileDataInteractor
+ impl: QRCodeScannerTileDataInteractor
): QSTileAvailabilityInteractor
companion object {
@@ -69,6 +70,7 @@
labelRes = R.string.qr_code_scanner_title,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject QR Code Scanner Tile into tileViewModelMap in QSModule. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
index cf1dca3..893ef7e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
@@ -29,12 +29,6 @@
/** Sets the activation state of Reduce Bright Colors */
void setReduceBrightColorsActivated(boolean activated);
- /** Sets whether Reduce Bright Colors is enabled */
- void setReduceBrightColorsFeatureAvailable(boolean enabled);
-
- /** Gets whether Reduce Bright Colors is enabled */
- boolean isReduceBrightColorsFeatureAvailable();
-
/** Gets whether Reduce Bright Colors is being transitioned to Even Dimmer */
boolean isInUpgradeMode(Resources resources);
/**
@@ -48,12 +42,5 @@
*/
default void onActivated(boolean activated) {
}
- /**
- * Listener invoked when the feature enabled state changes.
- *
- * @param enabled {@code true} if Reduce Bright Colors feature is enabled.
- */
- default void onFeatureEnabledChanged(boolean enabled) {
- }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
index 4d6cf78..ca6e40d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
@@ -48,7 +48,6 @@
private final ContentObserver mContentObserver;
private final SecureSettings mSecureSettings;
private final ArrayList<ReduceBrightColorsController.Listener> mListeners = new ArrayList<>();
- private boolean mAvailable = true;
@Inject
public ReduceBrightColorsControllerImpl(UserTracker userTracker,
@@ -77,7 +76,6 @@
mCurrentUserTrackerCallback = new UserTracker.Callback() {
@Override
public void onUserChanged(int newUser, Context userContext) {
- mAvailable = true;
synchronized (mListeners) {
if (mListeners.size() > 0) {
if (com.android.systemui.Flags.registerContentObserversAsync()) {
@@ -141,17 +139,6 @@
mManager.setReduceBrightColorsActivated(activated);
}
- @Override
- public void setReduceBrightColorsFeatureAvailable(boolean enabled) {
- mAvailable = enabled;
- dispatchOnEnabledChanged(enabled);
- mAvailable = true;
- }
-
- @Override
- public boolean isReduceBrightColorsFeatureAvailable() {
- return mAvailable;
- }
@Override
public boolean isInUpgradeMode(Resources resources) {
@@ -166,11 +153,4 @@
l.onActivated(activated);
}
}
-
- private void dispatchOnEnabledChanged(boolean enabled) {
- ArrayList<Listener> copy = new ArrayList<>(mListeners);
- for (Listener l : copy) {
- l.onFeatureEnabledChanged(enabled);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index c39ff55..c2f1c3d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint
import android.graphics.Rect
import android.os.Bundle
+import android.util.IndentingPrintWriter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -41,8 +42,8 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.layout
-import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.dimensionResource
@@ -59,7 +60,9 @@
import com.android.compose.modifiers.padding
import com.android.compose.modifiers.thenIf
import com.android.compose.theme.PlatformTheme
+import com.android.systemui.Dumpable
import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.dump.DumpManager
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
@@ -76,6 +79,10 @@
import com.android.systemui.qs.ui.composable.ShadeBody
import com.android.systemui.res.R
import com.android.systemui.util.LifecycleFragment
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printSection
+import com.android.systemui.util.println
+import java.io.PrintWriter
import java.util.function.Consumer
import javax.inject.Inject
import javax.inject.Named
@@ -91,9 +98,10 @@
@Inject
constructor(
private val qsFragmentComposeViewModelFactory: QSFragmentComposeViewModel.Factory,
+ private val dumpManager: DumpManager,
@Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
@Named(QS_PANEL) private val qsMediaHost: MediaHost,
-) : LifecycleFragment(), QS {
+) : LifecycleFragment(), QS, Dumpable {
private val scrollListener = MutableStateFlow<QS.ScrollListener?>(null)
private val heightListener = MutableStateFlow<QS.HeightListener?>(null)
@@ -118,8 +126,24 @@
var top by mutableStateOf(0)
var bottom by mutableStateOf(0)
var radius by mutableStateOf(0)
+
+ fun dump(pw: IndentingPrintWriter) {
+ pw.printSection("NotificationScrimClippingParams") {
+ pw.println("isEnabled", isEnabled)
+ pw.println("leftInset", "${leftInset}px")
+ pw.println("rightInset", "${rightInset}px")
+ pw.println("top", "${top}px")
+ pw.println("bottom", "${bottom}px")
+ pw.println("radius", "${radius}px")
+ }
+ }
}
+ override fun onStart() {
+ super.onStart()
+ registerDumpable()
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -343,11 +367,11 @@
}
override fun getHeaderTop(): Int {
- return viewModel.qqsHeaderHeight.value
+ return qqsPositionOnRoot.top
}
override fun getHeaderBottom(): Int {
- return headerTop + qqsHeight.value
+ return qqsPositionOnRoot.bottom
}
override fun getHeaderLeft(): Int {
@@ -358,7 +382,7 @@
outBounds.set(qqsPositionOnRoot)
view?.getBoundsOnScreen(composeViewPositionOnScreen)
?: run { composeViewPositionOnScreen.setEmpty() }
- qqsPositionOnRoot.offset(composeViewPositionOnScreen.left, composeViewPositionOnScreen.top)
+ outBounds.offset(composeViewPositionOnScreen.left, composeViewPositionOnScreen.top)
}
override fun isHeaderShown(): Boolean {
@@ -404,37 +428,29 @@
onDispose { qqsVisible.value = false }
}
Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) {
- Box(modifier = Modifier.fillMaxWidth()) {
+ Box(
+ modifier =
+ Modifier.fillMaxWidth()
+ .onPlaced { coordinates ->
+ val (leftFromRoot, topFromRoot) = coordinates.positionInRoot().round()
+ qqsPositionOnRoot.set(
+ leftFromRoot,
+ topFromRoot,
+ leftFromRoot + coordinates.size.width,
+ topFromRoot + coordinates.size.height,
+ )
+ }
+ .onSizeChanged { size -> qqsHeight.value = size.height }
+ .padding(top = { qqsPadding })
+ ) {
val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle()
if (qsEnabled) {
QuickQuickSettings(
viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel,
modifier =
- Modifier.onGloballyPositioned { coordinates ->
- val (leftFromRoot, topFromRoot) =
- coordinates.positionInRoot().round()
- val (width, height) = coordinates.size
- qqsPositionOnRoot.set(
- leftFromRoot,
- topFromRoot,
- leftFromRoot + width,
- topFromRoot + height
- )
- }
- .layout { measurable, constraints ->
- val placeable = measurable.measure(constraints)
- qqsHeight.value = placeable.height
-
- layout(placeable.width, placeable.height) {
- placeable.place(0, 0)
- }
- }
- .padding(top = { qqsPadding })
- .collapseExpandSemanticAction(
- stringResource(
- id = R.string.accessibility_quick_settings_expand
- )
- )
+ Modifier.collapseExpandSemanticAction(
+ stringResource(id = R.string.accessibility_quick_settings_expand)
+ )
)
}
}
@@ -486,6 +502,44 @@
}
} ?: this
}
+
+ private fun registerDumpable() {
+ val instanceId = instanceProvider.getNextId()
+ // Add an instanceId because the system may have more than 1 of these when re-inflating and
+ // DumpManager doesn't like repeated identifiers. Also, put it first because DumpHandler
+ // matches by end.
+ val stringId = "$instanceId-QSFragmentCompose"
+ lifecycleScope.launch {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
+ try {
+ dumpManager.registerNormalDumpable(stringId, this@QSFragmentCompose)
+ awaitCancellation()
+ } finally {
+ dumpManager.unregisterDumpable(stringId)
+ }
+ }
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.asIndenting().run {
+ notificationScrimClippingParams.dump(this)
+ printSection("QQS positioning") {
+ println("qqsHeight", "${headerHeight}px")
+ println("qqsTop", "${headerTop}px")
+ println("qqsBottom", "${headerBottom}px")
+ println("qqsLeft", "${headerLeft}px")
+ println("qqsPositionOnRoot", qqsPositionOnRoot)
+ val rect = Rect()
+ getHeaderBoundsOnScreen(rect)
+ println("qqsPositionOnScreen", rect)
+ }
+ println("QQS visible", qqsVisible.value)
+ if (::viewModel.isInitialized) {
+ printSection("View Model") { viewModel.dump(this@run, args) }
+ }
+ }
+ }
}
private fun View.setBackPressedDispatcher() {
@@ -526,3 +580,12 @@
}
}
}
+
+private val instanceProvider =
+ object {
+ private var currentId = 0
+
+ fun getNextId(): Int {
+ return currentId++
+ }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index 16133f4..7ab11d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -21,6 +21,7 @@
import androidx.annotation.FloatRange
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.systemui.Dumpable
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -34,10 +35,14 @@
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printSection
+import com.android.systemui.util.println
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import java.io.PrintWriter
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -62,7 +67,7 @@
private val configurationInteractor: ConfigurationInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
@Assisted private val lifecycleScope: LifecycleCoroutineScope,
-) {
+) : Dumpable {
val footerActionsViewModel =
footerActionsViewModelFactory.create(lifecycleScope).also {
lifecycleScope.launch { footerActionsController.init() }
@@ -228,6 +233,30 @@
*/
var collapseExpandAccessibilityAction: Runnable? = null
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.asIndenting().run {
+ printSection("Quick Settings state") {
+ println("isQSExpanded", isQSExpanded)
+ println("isQSVisible", isQSVisible)
+ println("isQSEnabled", qsEnabled.value)
+ println("isCustomizing", containerViewModel.editModeViewModel.isEditing.value)
+ }
+ printSection("Expansion state") {
+ println("qsExpansion", qsExpansionValue)
+ println("panelExpansionFraction", panelExpansionFractionValue)
+ println("squishinessFraction", squishinessFractionValue)
+ println("expansionState", expansionState.value)
+ }
+ printSection("Shade state") {
+ println("stackOverscrolling", stackScrollerOverscrollingValue)
+ println("statusBarState", StatusBarState.toString(statusBarState.value))
+ println("isSmallScreen", isSmallScreenValue)
+ println("heightOverride", "${heightOverrideValue}px")
+ println("qqsHeaderHeight", "${qqsHeaderHeight.value}px")
+ }
+ }
+ }
+
@AssistedFactory
interface Factory {
fun create(lifecycleScope: LifecycleCoroutineScope): QSFragmentComposeViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index cbcf68c..2f843ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -50,10 +50,12 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
@@ -95,6 +97,7 @@
// Bind retry control.
private static final int MAX_BIND_RETRIES = 5;
private static final long DEFAULT_BIND_RETRY_DELAY = 5 * DateUtils.SECOND_IN_MILLIS;
+ private static final long ACTIVE_TILE_BIND_RETRY_DELAY = 1 * DateUtils.SECOND_IN_MILLIS;
private static final long LOW_MEMORY_BIND_RETRY_DELAY = 20 * DateUtils.SECOND_IN_MILLIS;
private static final long TILE_SERVICE_ONCLICK_ALLOW_LIST_DEFAULT_DURATION_MS = 15_000;
private static final String PROPERTY_TILE_SERVICE_ONCLICK_ALLOW_LIST_DURATION =
@@ -107,6 +110,7 @@
private final Intent mIntent;
private final UserHandle mUser;
private final DelayableExecutor mExecutor;
+ private final SystemClock mSystemClock;
private final IBinder mToken = new Binder();
private final PackageManagerAdapter mPackageManagerAdapter;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -120,7 +124,6 @@
private IBinder mClickBinder;
private int mBindTryCount;
- private long mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
private AtomicBoolean isDeathRebindScheduled = new AtomicBoolean(false);
private AtomicBoolean mBound = new AtomicBoolean(false);
private AtomicBoolean mPackageReceiverRegistered = new AtomicBoolean(false);
@@ -138,7 +141,8 @@
TileLifecycleManager(@Main Handler handler, Context context, IQSService service,
PackageManagerAdapter packageManagerAdapter, BroadcastDispatcher broadcastDispatcher,
@Assisted Intent intent, @Assisted UserHandle user, ActivityManager activityManager,
- IDeviceIdleController deviceIdleController, @Background DelayableExecutor executor) {
+ IDeviceIdleController deviceIdleController, @Background DelayableExecutor executor,
+ SystemClock systemClock) {
mContext = context;
mHandler = handler;
mIntent = intent;
@@ -146,6 +150,7 @@
mIntent.putExtra(TileService.EXTRA_TOKEN, mToken);
mUser = user;
mExecutor = executor;
+ mSystemClock = systemClock;
mPackageManagerAdapter = packageManagerAdapter;
mBroadcastDispatcher = broadcastDispatcher;
mActivityManager = activityManager;
@@ -436,25 +441,31 @@
// If mBound is true (meaning that we should be bound), then reschedule binding for
// later.
if (mBound.get() && checkComponentState()) {
- if (isDeathRebindScheduled.compareAndSet(false, true)) {
+ if (isDeathRebindScheduled.compareAndSet(false, true)) { // if already not scheduled
+
+
mExecutor.executeDelayed(() -> {
// Only rebind if we are supposed to, but remove the scheduling anyway.
if (mBound.get()) {
setBindService(true);
}
- isDeathRebindScheduled.set(false);
+ isDeathRebindScheduled.set(false); // allow scheduling again
}, getRebindDelay());
}
}
});
}
+ private long mLastRebind = 0;
/**
* @return the delay to automatically rebind after a service died. It provides a longer delay if
* the device is a low memory state because the service is likely to get killed again by the
* system. In this case we want to rebind later and not to cause a loop of a frequent rebinds.
+ * It also provides a longer delay if called quickly (a few seconds) after a first call.
*/
private long getRebindDelay() {
+ final long now = mSystemClock.currentTimeMillis();
+
final ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
mActivityManager.getMemoryInfo(info);
@@ -462,7 +473,20 @@
if (info.lowMemory) {
delay = LOW_MEMORY_BIND_RETRY_DELAY;
} else {
- delay = mBindRetryDelay;
+ if (Flags.qsQuickRebindActiveTiles()) {
+ final long elapsedTimeSinceLastRebind = now - mLastRebind;
+ final boolean justAttemptedRebind =
+ elapsedTimeSinceLastRebind < DEFAULT_BIND_RETRY_DELAY;
+ if (isActiveTile() && !justAttemptedRebind) {
+ delay = ACTIVE_TILE_BIND_RETRY_DELAY;
+ } else {
+ delay = DEFAULT_BIND_RETRY_DELAY;
+ }
+ } else {
+ delay = DEFAULT_BIND_RETRY_DELAY;
+ }
+
+ mLastRebind = now;
}
if (mDebug) Log.i(TAG, "Rebinding with a delay=" + delay + " - " + getComponent());
return delay;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index d10471d..c5fa8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -44,7 +44,7 @@
/**
* Manages the priority which lets {@link TileServices} make decisions about which tiles
* to bind. Also holds on to and manages the {@link TileLifecycleManager}, informing it
- * of when it is allowed to bind based on decisions frome the {@link TileServices}.
+ * of when it is allowed to bind based on decisions from the {@link TileServices}.
*/
public class TileServiceManager {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
index 28c1fbf..2f054b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
@@ -24,10 +24,10 @@
import com.android.systemui.qs.panels.shared.model.EditTileData
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
@SysUISingleton
@@ -60,6 +60,7 @@
Icon.Loaded(icon, ContentDescription.Loaded(label.toString())),
Text.Loaded(label.toString()),
Text.Loaded(appName.toString()),
+ TileCategory.PROVIDED_BY_APP,
)
} else {
null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
index 3b29422..a2cee3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
@@ -24,6 +24,7 @@
import com.android.systemui.qs.panels.data.repository.StockTilesRepository
import com.android.systemui.qs.panels.domain.model.EditTilesModel
import com.android.systemui.qs.panels.shared.model.EditTileData
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import javax.inject.Inject
@@ -53,6 +54,7 @@
),
Text.Resource(config.uiConfig.labelRes),
null,
+ category = config.category,
)
} else {
EditTileData(
@@ -62,7 +64,8 @@
ContentDescription.Loaded(it.spec)
),
Text.Loaded(it.spec),
- null
+ null,
+ category = TileCategory.UNKNOWN,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
index 8b70bb9..b153ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
@@ -19,12 +19,14 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
data class EditTileData(
val tileSpec: TileSpec,
val icon: Icon,
val label: Text,
val appName: Text?,
+ val category: TileCategory,
) {
init {
check(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index d948dfd..c75b601 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.panels.ui.compose
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.runtime.Composable
@@ -24,7 +23,6 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
@@ -33,7 +31,6 @@
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.res.R
import javax.inject.Inject
@SysUISingleton
@@ -64,7 +61,7 @@
Tile(
tile = sizedTiles[index].tile,
iconOnly = iconTilesViewModel.isIconTile(sizedTiles[index].tile.spec),
- modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+ modifier = Modifier
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index a9027ff..eeb55ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -16,18 +16,15 @@
package com.android.systemui.qs.panels.ui.compose
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
-import com.android.systemui.res.R
@Composable
fun QuickQuickSettings(
@@ -54,11 +51,7 @@
key = { index -> sizedTiles[index].tile.spec.spec },
span = { index -> GridItemSpan(sizedTiles[index].width) }
) { index ->
- Tile(
- tile = tiles[index],
- iconOnly = sizedTiles[index].isIcon,
- modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
- )
+ Tile(tile = tiles[index], iconOnly = sizedTiles[index].isIcon, modifier = Modifier)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index 79c2eb9..93037d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -34,6 +34,7 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalOverscrollConfiguration
+import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
@@ -44,7 +45,6 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -96,6 +96,8 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.util.fastMap
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
import com.android.compose.modifiers.background
@@ -116,6 +118,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.toUiState
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.groupAndSort
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
import java.util.function.Supplier
@@ -136,23 +139,23 @@
// TODO(b/361789146): Draw the shapes instead of clipping
val tileShape = TileDefaults.animateTileShape(uiState.state)
- val iconShape = TileDefaults.animateIconShape(uiState.state)
TileContainer(
colors = colors,
showLabels = showLabels,
label = uiState.label,
iconOnly = iconOnly,
- shape = if (iconOnly) iconShape else tileShape,
+ shape = tileShape,
clickEnabled = true,
onClick = tile::onClick,
onLongClick = tile::onLongClick,
- modifier = modifier,
+ modifier = modifier.height(tileHeight()),
) {
val icon = getTileIcon(icon = uiState.icon)
if (iconOnly) {
TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center))
} else {
+ val iconShape = TileDefaults.animateIconShape(uiState.state)
LargeTileContent(
label = uiState.label,
secondaryLabel = uiState.secondaryLabel,
@@ -199,7 +202,7 @@
Expandable(
color = backgroundColor,
shape = shape,
- modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)).clip(shape)
+ modifier = Modifier.height(tileHeight()).clip(shape)
) {
Box(
modifier =
@@ -246,7 +249,7 @@
// Icon
Box(
modifier =
- Modifier.fillMaxHeight().aspectRatio(1f).thenIf(toggleClickSupported) {
+ Modifier.size(TileDefaults.ToggleTargetSize).thenIf(toggleClickSupported) {
Modifier.clip(iconShape)
.background(colors.iconBackground, { 1f })
.combinedClickable(onClick = onClick, onLongClick = onLongClick)
@@ -473,31 +476,39 @@
onClick: (TileSpec) -> Unit,
dragAndDropState: DragAndDropState,
) {
- // Available tiles aren't visible during drag and drop, so the row isn't needed
- val (otherTilesStock, otherTilesCustom) =
- tiles.map { TileGridCell(it, 0) }.partition { it.tile.appName == null }
val availableTileHeight = tileHeight(true)
val availableGridHeight = gridHeight(tiles.size, availableTileHeight, columns, tilePadding)
+ // Available tiles aren't visible during drag and drop, so the row isn't needed
+ val groupedTiles =
+ remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
+ groupAndSort(tiles.fastMap { TileGridCell(it, 0) })
+ }
+ val labelColors = TileDefaults.inactiveTileColors()
// Available tiles
TileLazyGrid(
modifier = Modifier.height(availableGridHeight).testTag(AVAILABLE_TILES_GRID_TEST_TAG),
columns = GridCells.Fixed(columns)
) {
- editTiles(
- otherTilesStock,
- ClickAction.ADD,
- onClick,
- dragAndDropState = dragAndDropState,
- showLabels = true,
- )
- editTiles(
- otherTilesCustom,
- ClickAction.ADD,
- onClick,
- dragAndDropState = dragAndDropState,
- showLabels = true,
- )
+ groupedTiles.forEach { category, tiles ->
+ stickyHeader {
+ Text(
+ text = category.label.load() ?: "",
+ fontSize = 20.sp,
+ color = labelColors.label,
+ modifier =
+ Modifier.background(Color.Black)
+ .padding(start = 16.dp, bottom = 8.dp, top = 8.dp)
+ )
+ }
+ editTiles(
+ tiles,
+ ClickAction.ADD,
+ onClick,
+ dragAndDropState = dragAndDropState,
+ showLabels = true,
+ )
+ }
}
}
@@ -619,7 +630,7 @@
showLabels: Boolean,
modifier: Modifier = Modifier,
) {
- val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
+ val label = tileViewModel.label.text
val colors = TileDefaults.inactiveTileColors()
TileContainer(
@@ -639,7 +650,7 @@
} else {
LargeTileContent(
label = label,
- secondaryLabel = tileViewModel.appName?.load(),
+ secondaryLabel = tileViewModel.appName?.text,
icon = tileViewModel.icon,
colors = colors,
iconShape = RoundedCornerShape(TileDefaults.InactiveCornerRadius),
@@ -673,7 +684,7 @@
animateToEnd: Boolean = false,
modifier: Modifier = Modifier,
) {
- val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
+ val iconModifier = modifier.size(TileDefaults.IconSize)
val context = LocalContext.current
val loadedDrawable =
remember(icon, context) {
@@ -710,17 +721,12 @@
}
}
-@Composable
private fun Modifier.tilePadding(): Modifier {
- return padding(dimensionResource(id = R.dimen.qs_label_container_margin))
+ return padding(TileDefaults.TilePadding)
}
-@Composable
private fun tileHorizontalArrangement(): Arrangement.Horizontal {
- return spacedBy(
- space = dimensionResource(id = R.dimen.qs_label_container_margin),
- alignment = Alignment.Start
- )
+ return spacedBy(space = TileDefaults.TileArrangementPadding, alignment = Alignment.Start)
}
@Composable
@@ -728,7 +734,7 @@
return if (iconWithLabel) {
TileDefaults.IconTileWithLabelHeight
} else {
- dimensionResource(id = R.dimen.qs_tile_height)
+ TileDefaults.TileHeight
}
}
@@ -749,6 +755,14 @@
val InactiveCornerRadius = 50.dp
val ActiveIconCornerRadius = 16.dp
val ActiveTileCornerRadius = 24.dp
+
+ val ToggleTargetSize = 56.dp
+ val IconSize = 24.dp
+
+ val TilePadding = 8.dp
+ val TileArrangementPadding = 6.dp
+
+ val TileHeight = 72.dp
val IconTileWithLabelHeight = 140.dp
/** An active tile without dual target uses the active color as background */
@@ -812,7 +826,7 @@
fun animateIconShape(state: Int): Shape {
return animateShape(
state = state,
- activeCornerRadius = ActiveTileCornerRadius,
+ activeCornerRadius = ActiveIconCornerRadius,
label = "QSTileCornerRadius",
)
}
@@ -821,7 +835,7 @@
fun animateTileShape(state: Int): Shape {
return animateShape(
state = state,
- activeCornerRadius = ActiveIconCornerRadius,
+ activeCornerRadius = ActiveTileCornerRadius,
label = "QSTileIconCornerRadius",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
index 8ca8de7..08ee856 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
@@ -21,6 +21,7 @@
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.shared.model.CategoryAndName
/** Represents an item from a grid associated with a row and a span */
interface GridCell {
@@ -38,7 +39,7 @@
override val row: Int,
override val width: Int,
override val span: GridItemSpan = GridItemSpan(width)
-) : GridCell, SizedTile<EditTileViewModel> {
+) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile {
val key: String = "${tile.tileSpec.spec}-$row"
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 42715be..4a8aa83e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -16,6 +16,9 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import android.content.Context
+import androidx.compose.ui.util.fastMap
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.qs.panels.domain.interactor.EditTilesListInteractor
@@ -27,6 +30,7 @@
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
@@ -35,6 +39,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@@ -49,6 +54,8 @@
private val currentTilesInteractor: CurrentTilesInteractor,
private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
private val minTilesInteractor: MinimumTilesInteractor,
+ private val configurationInteractor: ConfigurationInteractor,
+ @Application private val applicationContext: Context,
@Named("Default") private val defaultGridLayout: GridLayout,
@Application private val applicationScope: CoroutineScope,
gridLayoutTypeInteractor: GridLayoutTypeInteractor,
@@ -99,38 +106,45 @@
.map { it.tileSpec }
.minus(currentTilesInteractor.currentTilesSpecs.toSet())
)
- currentTilesInteractor.currentTiles.map { tiles ->
- val currentSpecs = tiles.map { it.spec }
- val canRemoveTiles = currentSpecs.size > minimumTiles
- val allTiles = editTilesData.stockTiles + editTilesData.customTiles
- val allTilesMap = allTiles.associate { it.tileSpec to it }
- val currentTiles = currentSpecs.map { allTilesMap.get(it) }.filterNotNull()
- val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs }
+ currentTilesInteractor.currentTiles
+ .map { tiles ->
+ val currentSpecs = tiles.map { it.spec }
+ val canRemoveTiles = currentSpecs.size > minimumTiles
+ val allTiles = editTilesData.stockTiles + editTilesData.customTiles
+ val allTilesMap = allTiles.associate { it.tileSpec to it }
+ val currentTiles = currentSpecs.map { allTilesMap.get(it) }.filterNotNull()
+ val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs }
- (currentTiles + nonCurrentTiles)
- .filterNot { it.tileSpec in unavailable }
- .map {
- val current = it.tileSpec in currentSpecs
- val availableActions = buildSet {
- if (current) {
- add(AvailableEditActions.MOVE)
- if (canRemoveTiles) {
- add(AvailableEditActions.REMOVE)
+ (currentTiles + nonCurrentTiles)
+ .filterNot { it.tileSpec in unavailable }
+ .map {
+ val current = it.tileSpec in currentSpecs
+ val availableActions = buildSet {
+ if (current) {
+ add(AvailableEditActions.MOVE)
+ if (canRemoveTiles) {
+ add(AvailableEditActions.REMOVE)
+ }
+ } else {
+ add(AvailableEditActions.ADD)
}
- } else {
- add(AvailableEditActions.ADD)
}
+ UnloadedEditTileViewModel(
+ it.tileSpec,
+ it.icon,
+ it.label,
+ it.appName,
+ current,
+ availableActions,
+ it.category,
+ )
}
- EditTileViewModel(
- it.tileSpec,
- it.icon,
- it.label,
- it.appName,
- current,
- availableActions
- )
- }
- }
+ }
+ .combine(configurationInteractor.onAnyConfigurationChange.emitOnStart()) {
+ tiles,
+ _ ->
+ tiles.fastMap { it.load(applicationContext) }
+ }
} else {
emptyFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
index a4c8638..ee12736f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
@@ -16,9 +16,15 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import android.content.Context
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.text.AnnotatedString
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.ui.compose.toAnnotatedString
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.CategoryAndName
+import com.android.systemui.qs.shared.model.TileCategory
/**
* View model for each tile that is available to be added/removed/moved in Edit mode.
@@ -26,14 +32,41 @@
* [isCurrent] indicates whether this tile is part of the current set of tiles that the user sees in
* Quick Settings.
*/
-data class EditTileViewModel(
+data class UnloadedEditTileViewModel(
val tileSpec: TileSpec,
val icon: Icon,
val label: Text,
val appName: Text?,
val isCurrent: Boolean,
val availableEditActions: Set<AvailableEditActions>,
-)
+ val category: TileCategory,
+) {
+ fun load(context: Context): EditTileViewModel {
+ return EditTileViewModel(
+ tileSpec,
+ icon,
+ label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
+ appName?.toAnnotatedString(context),
+ isCurrent,
+ availableEditActions,
+ category,
+ )
+ }
+}
+
+@Immutable
+data class EditTileViewModel(
+ val tileSpec: TileSpec,
+ val icon: Icon,
+ val label: AnnotatedString,
+ val appName: AnnotatedString?,
+ val isCurrent: Boolean,
+ val availableEditActions: Set<AvailableEditActions>,
+ override val category: TileCategory,
+) : CategoryAndName {
+ override val name
+ get() = label.text
+}
enum class AvailableEditActions {
ADD,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSTileListLog.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSTileListLog.kt
index c56ca8c..d50374b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSTileListLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSTileListLog.kt
@@ -15,9 +15,7 @@
*/
package com.android.systemui.qs.pipeline.dagger
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
import javax.inject.Qualifier
/** A [LogBuffer] for the new QS Pipeline for logging changes to the set of current tiles. */
-@Qualifier @MustBeDocumented @Retention(RetentionPolicy.RUNTIME) annotation class QSTileListLog
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class QSTileListLog
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt
index 4d823ab..bde6820 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt
@@ -42,7 +42,7 @@
) :
CallbackControllerAutoAddable<
ReduceBrightColorsController.Listener,
- ReduceBrightColorsController
+ ReduceBrightColorsController,
>(controller) {
override val spec: TileSpec
@@ -55,12 +55,6 @@
sendAdd()
}
}
-
- override fun onFeatureEnabledChanged(enabled: Boolean) {
- if (!enabled) {
- available = false
- }
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt b/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt
new file mode 100644
index 0000000..59cb7d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.qs.shared.model
+
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.res.R
+
+/** Categories for tiles. This can be used to sort tiles in edit mode. */
+enum class TileCategory(val label: Text) {
+ CONNECTIVITY(Text.Resource(R.string.qs_edit_mode_category_connectivity)),
+ UTILITIES(Text.Resource(R.string.qs_edit_mode_category_utilities)),
+ DISPLAY(Text.Resource(R.string.qs_edit_mode_category_display)),
+ PRIVACY(Text.Resource(R.string.qs_edit_mode_category_privacy)),
+ ACCESSIBILITY(Text.Resource(R.string.qs_edit_mode_category_accessibility)),
+ PROVIDED_BY_APP(Text.Resource(R.string.qs_edit_mode_category_providedByApps)),
+ UNKNOWN(Text.Resource(R.string.qs_edit_mode_category_unknown)),
+}
+
+interface CategoryAndName {
+ val category: TileCategory
+ val name: String
+}
+
+/**
+ * Groups the elements of the list by [CategoryAndName.category] (with the keys sorted in the
+ * natural order of [TileCategory]), and sorts the elements of each group based on the
+ * [CategoryAndName.name].
+ */
+fun <T : CategoryAndName> groupAndSort(list: List<T>): Map<TileCategory, List<T>> {
+ val groupedByCategory = list.groupBy { it.category }.toSortedMap()
+ return groupedByCategory.mapValues { it.value.sortedBy { it.name } }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index b96e83d..f723ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.LAUNCH_SOURCE_QS_TILE;
+
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
@@ -96,7 +98,7 @@
@Override
protected void handleClick(@Nullable Expandable expandable) {
- mUiHandler.post(() -> mDialogManager.showDialog(expandable));
+ mUiHandler.post(() -> mDialogManager.showDialog(expandable, LAUNCH_SOURCE_QS_TILE));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index 6d63d26..7d23fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -21,6 +21,8 @@
import android.os.Handler
import android.os.Looper
import android.service.quicksettings.Tile
+import androidx.annotation.DrawableRes
+import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.repeatOnLifecycle
@@ -62,7 +64,7 @@
activityStarter: ActivityStarter,
qsLogger: QSLogger,
qsTileConfigProvider: QSTileConfigProvider,
- dataInteractor: ModesTileDataInteractor,
+ private val dataInteractor: ModesTileDataInteractor,
private val tileMapper: ModesTileMapper,
private val userActionInteractor: ModesTileUserActionInteractor,
) :
@@ -98,7 +100,7 @@
override fun newTileState(): QSTile.State {
return QSTile.State().apply {
label = mContext.getString(R.string.quick_settings_modes_label)
- icon = ResourceIcon.get(R.drawable.qs_dnd_icon_off)
+ icon = ResourceIcon.get(ICON_RES_ID)
state = Tile.STATE_INACTIVE
}
}
@@ -109,23 +111,26 @@
override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
- override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
- if (arg is ModesTileModel) {
- tileState = tileMapper.map(config, arg)
+ @VisibleForTesting
+ public override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
+ // This runBlocking() will block @Background. Due to caches, it's expected to be fast.
+ val model =
+ if (arg is ModesTileModel) arg else runBlocking { dataInteractor.getCurrentTileModel() }
- state?.apply {
- this.state = tileState.activationState.legacyState
- val tileStateIcon = tileState.icon()
- icon = tileStateIcon?.asQSTileIcon() ?: ResourceIcon.get(R.drawable.qs_dnd_icon_off)
- label = tileLabel
- secondaryLabel = tileState.secondaryLabel
- contentDescription = tileState.contentDescription
- expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
- }
+ tileState = tileMapper.map(config, model)
+ state?.apply {
+ this.state = tileState.activationState.legacyState
+ val tileStateIcon = tileState.icon()
+ icon = tileStateIcon?.asQSTileIcon() ?: ResourceIcon.get(ICON_RES_ID)
+ label = tileLabel
+ secondaryLabel = tileState.secondaryLabel
+ contentDescription = tileState.contentDescription
+ expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
}
}
companion object {
const val TILE_SPEC = "dnd"
+ @DrawableRes val ICON_RES_ID = com.android.internal.R.drawable.ic_zen_priority_modes
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index a3feb2b..d89e73d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -43,12 +43,14 @@
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.recordissue.IssueRecordingService
+import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent
+import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
import com.android.systemui.recordissue.TraceurMessageSender
import com.android.systemui.res.R
+import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.RecordingService
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
@@ -56,6 +58,9 @@
import java.util.concurrent.Executor
import javax.inject.Inject
+const val DELAY_MS: Long = 0
+const val INTERVAL_MS: Long = 1000
+
class RecordIssueTile
@Inject
constructor(
@@ -77,6 +82,7 @@
@Background private val bgExecutor: Executor,
private val issueRecordingState: IssueRecordingState,
private val delegateFactory: RecordIssueDialogDelegate.Factory,
+ private val recordingController: RecordingController,
) :
QSTileImpl<QSTile.BooleanState>(
host,
@@ -132,23 +138,25 @@
}
private fun startIssueRecordingService() =
- PendingIntent.getForegroundService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStartIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+ recordingController.startCountdown(
+ DELAY_MS,
+ INTERVAL_MS,
+ pendingServiceIntent(getStartIntent(userContextProvider.userContext)),
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext))
+ )
private fun stopIssueRecordingService() =
- PendingIntent.getService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStopIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext))
.send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+ private fun pendingServiceIntent(action: Intent) =
+ PendingIntent.getService(
+ userContextProvider.userContext,
+ RecordingService.REQUEST_CODE,
+ action,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+
private fun showPrompt(expandable: Expandable?) {
val dialog: AlertDialog =
delegateFactory
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index af5b311..d624d98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -29,6 +29,7 @@
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.accessibility.extradim.ExtraDimDialogManager;
import com.android.systemui.animation.Expandable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -55,6 +56,7 @@
private final ReduceBrightColorsController mReduceBrightColorsController;
private boolean mIsListening;
private final boolean mInUpgradeMode;
+ private final ExtraDimDialogManager mExtraDimDialogManager;
@Inject
public ReduceBrightColorsTile(
@@ -68,12 +70,14 @@
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
- QSLogger qsLogger
+ QSLogger qsLogger,
+ ExtraDimDialogManager extraDimDialogManager
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mReduceBrightColorsController = reduceBrightColorsController;
mReduceBrightColorsController.observe(getLifecycle(), this);
+ mExtraDimDialogManager = extraDimDialogManager;
mInUpgradeMode = reduceBrightColorsController.isInUpgradeMode(mContext.getResources());
mIsAvailable = isAvailable || mInUpgradeMode;
@@ -102,19 +106,24 @@
private boolean goToEvenDimmer() {
if (mInUpgradeMode) {
- mHost.removeTile(getTileSpec());
- mIsAvailable = false;
return true;
}
return false;
}
@Override
- protected void handleClick(@Nullable Expandable expandable) {
-
+ protected void handleLongClick(@Nullable Expandable expandable) {
if (goToEvenDimmer()) {
- mActivityStarter.postStartActivityDismissingKeyguard(
- new Intent(Settings.ACTION_DISPLAY_SETTINGS), 0);
+ mExtraDimDialogManager.dismissKeyguardIfNeededAndShowDialog(expandable);
+ } else {
+ super.handleLongClick(expandable);
+ }
+ }
+
+ @Override
+ protected void handleClick(@Nullable Expandable expandable) {
+ if (goToEvenDimmer()) {
+ mExtraDimDialogManager.dismissKeyguardIfNeededAndShowDialog(expandable);
} else {
mReduceBrightColorsController.setReduceBrightColorsActivated(!mState.value);
}
@@ -146,9 +155,4 @@
public void onActivated(boolean activated) {
refreshState();
}
-
- @Override
- public void onFeatureEnabledChanged(boolean enabled) {
- refreshState();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 71f8639..89b9eee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -306,6 +306,8 @@
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(context));
mWifiRecyclerView.setAdapter(mAdapter);
+
+ updateDialogUI(getWifiNetworkContent());
}
@Override
@@ -315,6 +317,7 @@
}
mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
+
mInternetDialogController.onStart(this, mCanConfigWifi);
if (!mCanConfigWifi) {
hideWifiViews();
@@ -402,10 +405,12 @@
internetContent.mShouldUpdateMobileNetwork = shouldUpdateMobileNetwork;
internetContent.mInternetDialogTitleString = getDialogTitleText();
internetContent.mInternetDialogSubTitle = getSubtitleText();
- internetContent.mActiveNetworkIsCellular =
- mInternetDialogController.activeNetworkIsCellular();
- internetContent.mIsCarrierNetworkActive =
- mInternetDialogController.isCarrierNetworkActive();
+ if (shouldUpdateMobileNetwork) {
+ internetContent.mActiveNetworkIsCellular =
+ mInternetDialogController.activeNetworkIsCellular();
+ internetContent.mIsCarrierNetworkActive =
+ mInternetDialogController.isCarrierNetworkActive();
+ }
internetContent.mIsAirplaneModeEnabled = mInternetDialogController.isAirplaneModeEnabled();
internetContent.mHasEthernet = mInternetDialogController.hasEthernet();
internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled();
@@ -416,6 +421,15 @@
return internetContent;
}
+ private InternetContent getWifiNetworkContent() {
+ InternetContent internetContent = new InternetContent();
+ internetContent.mInternetDialogTitleString = getDialogTitleText();
+ internetContent.mInternetDialogSubTitle = getSubtitleText();
+ internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled();
+ internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked();
+ return internetContent;
+ }
+
private void setOnClickListener(SystemUIDialog dialog) {
mMobileNetworkLayout.setOnClickListener(v -> {
int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
index 4971fef..0c8a375 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
@@ -19,6 +19,7 @@
import android.app.AlertDialog
import android.app.BroadcastOptions
import android.app.PendingIntent
+import android.content.Intent
import android.util.Log
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.DialogCuj
@@ -27,12 +28,16 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.DELAY_MS
+import com.android.systemui.qs.tiles.INTERVAL_MS
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
-import com.android.systemui.recordissue.IssueRecordingService
+import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent
+import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
+import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.RecordingService
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
@@ -53,6 +58,7 @@
private val panelInteractor: PanelInteractor,
private val userContextProvider: UserContextProvider,
private val delegateFactory: RecordIssueDialogDelegate.Factory,
+ private val recordingController: RecordingController,
) : QSTileUserActionInteractor<IssueRecordingModel> {
override suspend fun handleInput(input: QSTileInput<IssueRecordingModel>) {
@@ -95,20 +101,22 @@
}
private fun startIssueRecordingService() =
- PendingIntent.getForegroundService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStartIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+ recordingController.startCountdown(
+ DELAY_MS,
+ INTERVAL_MS,
+ pendingServiceIntent(getStartIntent(userContextProvider.userContext)),
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext))
+ )
private fun stopIssueRecordingService() =
- PendingIntent.getService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStopIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext))
.send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+
+ private fun pendingServiceIntent(action: Intent) =
+ PendingIntent.getService(
+ userContextProvider.userContext,
+ RecordingService.REQUEST_CODE,
+ action,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 3f18fc2..483373d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -20,11 +20,16 @@
import android.content.Context
import android.os.UserHandle
import com.android.app.tracing.coroutines.flow.map
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.ModesTile
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
+import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -52,18 +57,47 @@
*/
fun tileData() =
zenModeInteractor.activeModes
- .map { modes ->
- ModesTileModel(
- isActivated = modes.isNotEmpty(),
- icon =
- if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons())
- zenModeInteractor.getActiveModeIcon(modes)
- else null,
- activeModes = modes.map { it.name }
- )
- }
+ .map { activeModes -> buildTileData(activeModes) }
.flowOn(bgDispatcher)
.distinctUntilChanged()
+ suspend fun getCurrentTileModel() = buildTileData(zenModeInteractor.getActiveModes())
+
+ private fun buildTileData(activeModes: ActiveZenModes): ModesTileModel {
+ if (usesModeIcons()) {
+ val tileIcon = getTileIcon(activeModes.mainMode)
+ return ModesTileModel(
+ isActivated = activeModes.isAnyActive(),
+ icon = tileIcon.icon,
+ iconResId = tileIcon.resId,
+ activeModes = activeModes.modeNames
+ )
+ } else {
+ return ModesTileModel(
+ isActivated = activeModes.isAnyActive(),
+ icon = context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(),
+ iconResId = ModesTile.ICON_RES_ID,
+ activeModes = activeModes.modeNames
+ )
+ }
+ }
+
+ private data class TileIcon(val icon: Icon.Loaded, val resId: Int?)
+
+ private fun getTileIcon(activeMode: ZenModeInfo?): TileIcon {
+ return if (activeMode != null) {
+ // ZenIconKey.resPackage is null if its resId is a system icon.
+ if (activeMode.icon.key.resPackage == null) {
+ TileIcon(activeMode.icon.drawable.asIcon(), activeMode.icon.key.resId)
+ } else {
+ TileIcon(activeMode.icon.drawable.asIcon(), null)
+ }
+ } else {
+ TileIcon(context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(), ModesTile.ICON_RES_ID)
+ }
+ }
+
override fun availability(user: UserHandle): Flow<Boolean> = flowOf(Flags.modesUi())
+
+ private fun usesModeIcons() = Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
index 904ff3a..db48123 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
@@ -21,5 +21,12 @@
data class ModesTileModel(
val isActivated: Boolean,
val activeModes: List<String>,
- val icon: Icon? = null
+ val icon: Icon.Loaded,
+
+ /**
+ * Resource id corresponding to [icon]. Will only be present if it's know to correspond to a
+ * resource with a known id in SystemUI (such as resources from `android.R`,
+ * `com.android.internal.R`, or `com.android.systemui.res` itself).
+ */
+ val iconResId: Int? = null
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index 83c3335..69da313 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -16,11 +16,9 @@
package com.android.systemui.qs.tiles.impl.modes.ui
-import android.app.Flags
import android.content.res.Resources
import android.icu.text.MessageFormat
import android.widget.Button
-import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
@@ -38,15 +36,8 @@
) : QSTileDataToStateMapper<ModesTileModel> {
override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons() && data.icon != null) {
- icon = { data.icon }
- } else {
- val iconRes =
- if (data.isActivated) R.drawable.qs_dnd_icon_on else R.drawable.qs_dnd_icon_off
- val icon = resources.getDrawable(iconRes, theme).asIcon()
- this.iconRes = iconRes
- this.icon = { icon }
- }
+ iconRes = data.iconResId
+ icon = { data.icon }
activationState =
if (data.isActivated) {
QSTileState.ActivationState.ACTIVE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
index 00b1e41..536c5f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
@@ -23,13 +23,13 @@
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
-import com.android.systemui.util.kotlin.isAvailable
import com.android.systemui.util.kotlin.isEnabled
import javax.inject.Inject
import javax.inject.Named
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -44,7 +44,7 @@
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<ReduceBrightColorsTileModel> {
return reduceBrightColorsController
.isEnabled()
@@ -53,6 +53,5 @@
.flowOn(bgCoroutineContext)
}
- override fun availability(user: UserHandle): Flow<Boolean> =
- reduceBrightColorsController.isAvailable()
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(isAvailable)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
index de49e70..15c9901 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
@@ -19,6 +19,7 @@
import android.content.Intent
import android.content.res.Resources
import android.provider.Settings
+import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.ReduceBrightColorsController
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
@@ -35,6 +36,7 @@
@Main private val resources: Resources,
private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
private val reduceBrightColorsController: ReduceBrightColorsController,
+ private val extraDimDialogManager: ExtraDimDialogManager,
) : QSTileUserActionInteractor<ReduceBrightColorsTileModel> {
val isInUpgradeMode: Boolean = reduceBrightColorsController.isInUpgradeMode(resources)
@@ -44,12 +46,9 @@
when (action) {
is QSTileUserAction.Click -> {
if (isInUpgradeMode) {
- reduceBrightColorsController.setReduceBrightColorsFeatureAvailable(false)
- qsTileIntentUserActionHandler.handle(
- action.expandable,
- Intent(Settings.ACTION_DISPLAY_SETTINGS)
+ extraDimDialogManager.dismissKeyguardIfNeededAndShowDialog(
+ action.expandable
)
- // TODO(b/349458355): show dialog
return@with
}
reduceBrightColorsController.setReduceBrightColorsActivated(
@@ -58,17 +57,14 @@
}
is QSTileUserAction.LongClick -> {
if (isInUpgradeMode) {
- reduceBrightColorsController.setReduceBrightColorsFeatureAvailable(false)
- qsTileIntentUserActionHandler.handle(
- action.expandable,
- Intent(Settings.ACTION_DISPLAY_SETTINGS)
+ extraDimDialogManager.dismissKeyguardIfNeededAndShowDialog(
+ action.expandable
)
- // TODO(b/349458355): show dialog
return@with
}
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS)
+ Intent(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS),
)
}
is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index cdcefdb..3a9cb17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -21,6 +21,7 @@
import androidx.annotation.StringRes
import com.android.internal.logging.InstanceId
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
data class QSTileConfig
@JvmOverloads
@@ -28,6 +29,7 @@
val tileSpec: TileSpec,
val uiConfig: QSTileUIConfig,
val instanceId: InstanceId,
+ val category: TileCategory,
val metricsSpec: String = tileSpec.spec,
val policy: QSTilePolicy = QSTilePolicy.NoRestrictions,
val autoRemoveOnUnavailable: Boolean = true,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
index 0609e79..4dbddd9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import javax.inject.Inject
interface QSTileConfigProvider {
@@ -73,6 +74,7 @@
spec,
QSTileUIConfig.Empty,
qsEventLogger.getNewInstanceId(),
+ category = TileCategory.PROVIDED_BY_APP,
)
is TileSpec.Invalid ->
throw IllegalArgumentException("TileSpec.Invalid doesn't support configs")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt
index 93bf73f..f77386d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt
@@ -17,21 +17,29 @@
package com.android.systemui.qs.ui.viewmodel
import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
/**
* Models UI state needed for rendering the content of the quick settings scene.
*
- * Different from [QuickSettingsSceneActionsViewModel] that models the UI state needed to figure out
+ * Different from [QuickSettingsUserActionsViewModel] that models the UI state needed to figure out
* which user actions can trigger navigation to other scenes.
*/
class QuickSettingsSceneContentViewModel
@@ -43,7 +51,9 @@
private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
private val footerActionsController: FooterActionsController,
val mediaCarouselInteractor: MediaCarouselInteractor,
-) {
+ private val shadeInteractor: ShadeInteractor,
+ private val sceneInteractor: SceneInteractor,
+) : ExclusiveActivatable() {
val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation
@@ -56,6 +66,19 @@
return footerActionsViewModelFactory.create(lifecycleOwner)
}
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch {
+ shadeInteractor.shadeMode.collect { shadeMode ->
+ if (shadeMode == ShadeMode.Split) {
+ sceneInteractor.snapToScene(Scenes.Shade, "Unfold while on QS")
+ }
+ }
+ }
+ awaitCancellation()
+ }
+ }
+
@AssistedFactory
interface Factory {
fun create(): QuickSettingsSceneContentViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
new file mode 100644
index 0000000..61c4c8c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** Models the UI state for the user actions for navigating to other scenes or overlays. */
+class QuickSettingsShadeOverlayActionsViewModel @AssistedInject constructor() :
+ UserActionsViewModel() {
+
+ override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+ setActions(
+ buildMap {
+ put(Swipe.Up, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
+ put(Back, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
+ }
+ )
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): QuickSettingsShadeOverlayActionsViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
new file mode 100644
index 0000000..3b97d82
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Models UI state used to render the content of the quick settings shade overlay.
+ *
+ * Different from [QuickSettingsShadeOverlayActionsViewModel], which only models user actions that
+ * can be performed to navigate to other scenes.
+ */
+class QuickSettingsShadeOverlayContentViewModel
+@AssistedInject
+constructor(
+ val sceneInteractor: SceneInteractor,
+ val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
+ val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
+) {
+ fun onScrimClicked() {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = "Shade scrim clicked",
+ )
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): QuickSettingsShadeOverlayContentViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
index 924a939..d01b33b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
@@ -14,25 +14,20 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.qs.ui.viewmodel
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
/**
* Models UI state used to render the content of the quick settings shade scene.
*
- * Different from [QuickSettingsShadeSceneActionsViewModel], which only models user actions that can
+ * Different from [QuickSettingsShadeUserActionsViewModel], which only models user actions that can
* be performed to navigate to other scenes.
*/
class QuickSettingsShadeSceneContentViewModel
@AssistedInject
constructor(
- val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
index 9956a46..bd1872d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
@@ -14,21 +14,20 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.qs.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeAlignment
+import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
/**
@@ -37,24 +36,23 @@
* Different from the [QuickSettingsShadeSceneContentViewModel] which models the _content_ of the
* scene.
*/
-class QuickSettingsShadeSceneActionsViewModel
+class QuickSettingsShadeUserActionsViewModel
@AssistedInject
constructor(
- private val shadeInteractor: ShadeInteractor,
val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
-) : SceneActionsViewModel() {
+) : UserActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
quickSettingsContainerViewModel.editModeViewModel.isEditing
.map { editing ->
buildMap {
+ put(Swipe.Up, UserActionResult(SceneFamilies.Home))
put(
- if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
- Swipe.Up
- } else {
- Swipe.Down
- },
- UserActionResult(SceneFamilies.Home)
+ Swipe(
+ direction = SwipeDirection.Down,
+ fromSource = SceneContainerEdge.TopLeft
+ ),
+ ReplaceByOverlay(Overlays.NotificationsShade)
)
if (!editing) {
put(Back, UserActionResult(SceneFamilies.Home))
@@ -66,6 +64,6 @@
@AssistedFactory
interface Factory {
- fun create(): QuickSettingsShadeSceneActionsViewModel
+ fun create(): QuickSettingsShadeUserActionsViewModel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
index 2bb5dc66..54e5cac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
@@ -27,7 +27,7 @@
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
@@ -42,12 +42,12 @@
* Different from [QuickSettingsSceneContentViewModel] that models UI state needed for rendering the
* content of the quick settings scene.
*/
-class QuickSettingsSceneActionsViewModel
+class QuickSettingsUserActionsViewModel
@AssistedInject
constructor(
private val qsSceneAdapter: QSSceneAdapter,
sceneBackInteractor: SceneBackInteractor,
-) : SceneActionsViewModel() {
+) : UserActionsViewModel() {
private val backScene: Flow<SceneKey> =
sceneBackInteractor.backScene
@@ -82,6 +82,6 @@
@AssistedFactory
interface Factory {
- fun create(): QuickSettingsSceneActionsViewModel
+ fun create(): QuickSettingsUserActionsViewModel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index fe5cbb1..a402a9d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -25,7 +25,6 @@
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
-import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
@@ -76,7 +75,6 @@
import androidx.annotation.NonNull;
-import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -404,23 +402,16 @@
@Override
public void notifyAccessibilityButtonClicked(int displayId) {
verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonClicked", () ->
- AccessibilityManager.getInstance(mContext)
- .notifyAccessibilityButtonClicked(displayId));
+ AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
+ displayId));
}
@Override
public void notifyAccessibilityButtonLongClicked() {
- verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonLongClicked",
- () -> {
- final Intent intent =
- new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
- final String chooserClassName = AccessibilityButtonChooserActivity
- .class.getName();
- intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
- });
+ verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonLongClicked", () ->
+ AccessibilityManager.getInstance(mContext)
+ .notifyAccessibilityButtonLongClicked(
+ mDisplayTracker.getDefaultDisplayId()));
}
@Override
@@ -894,11 +885,21 @@
return;
}
mHandler.removeCallbacks(mConnectionRunnable);
+
+ // Avoid creating TouchInteractionService because the System user in HSUM mode does not
+ // interact with UI elements
+ UserHandle currentUser = UserHandle.of(mUserTracker.getUserId());
+ if (UserManager.isHeadlessSystemUserMode() && currentUser.isSystem()) {
+ Log.w(TAG_OPS,
+ "Skipping connection to TouchInteractionService for the System user in HSUM "
+ + "mode.");
+ return;
+ }
try {
mBound = mContext.bindServiceAsUser(mQuickStepIntent,
mOverviewServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
- UserHandle.of(mUserTracker.getUserId()));
+ currentUser);
} catch (SecurityException e) {
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
index 14dfcc5..b0eaf9f 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
@@ -21,6 +21,10 @@
import com.android.traceur.PresetTraceConfigs.getDefaultConfig
import com.android.traceur.TraceConfig
+/**
+ * This class encapsulates the values that go into a customized record issue trace config, part of
+ * the RecordIssueTile feature. This class stores the last configuration chosen by power users.
+ */
class CustomTraceState(private val prefs: SharedPreferences) {
private var enabledTags: Set<String>?
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 3d6d00e..a5f4a89 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -23,8 +23,6 @@
import android.content.res.Resources
import android.net.Uri
import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
import android.util.Log
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogTransitionAnimator
@@ -50,11 +48,11 @@
notificationManager: NotificationManager,
userContextProvider: UserContextProvider,
keyguardDismissUtil: KeyguardDismissUtil,
- private val dialogTransitionAnimator: DialogTransitionAnimator,
- private val panelInteractor: PanelInteractor,
- private val traceurMessageSender: TraceurMessageSender,
+ dialogTransitionAnimator: DialogTransitionAnimator,
+ panelInteractor: PanelInteractor,
+ traceurMessageSender: TraceurMessageSender,
private val issueRecordingState: IssueRecordingState,
- private val iActivityManager: IActivityManager,
+ iActivityManager: IActivityManager,
) :
RecordingService(
controller,
@@ -66,6 +64,18 @@
keyguardDismissUtil
) {
+ private val commandHandler =
+ IssueRecordingServiceCommandHandler(
+ bgExecutor,
+ dialogTransitionAnimator,
+ panelInteractor,
+ traceurMessageSender,
+ issueRecordingState,
+ iActivityManager,
+ notificationManager,
+ userContextProvider,
+ )
+
override fun getTag(): String = TAG
override fun getChannelId(): String = CHANNEL_ID
@@ -76,10 +86,7 @@
Log.d(getTag(), "handling action: ${intent?.action}")
when (intent?.action) {
ACTION_START -> {
- bgExecutor.execute {
- traceurMessageSender.startTracing(issueRecordingState.traceConfig)
- }
- issueRecordingState.isRecording = true
+ commandHandler.handleStartCommand()
if (!issueRecordingState.recordScreen) {
// If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action
// will circumvent the RecordingService's screen recording start code.
@@ -87,41 +94,13 @@
}
}
ACTION_STOP,
- ACTION_STOP_NOTIF -> {
- // ViewCapture needs to save it's data before it is disabled, or else the data will
- // be lost. This is expected to change in the near future, and when that happens
- // this line should be removed.
- bgExecutor.execute {
- if (issueRecordingState.traceConfig.longTrace) {
- Settings.Global.putInt(
- contentResolver,
- NOTIFY_SESSION_ENDED_SETTING,
- DISABLED
- )
- }
- traceurMessageSender.stopTracing()
- }
- issueRecordingState.isRecording = false
- }
+ ACTION_STOP_NOTIF -> commandHandler.handleStopCommand(contentResolver)
ACTION_SHARE -> {
- bgExecutor.execute {
- mNotificationManager.cancelAsUser(
- null,
- intent.getIntExtra(EXTRA_NOTIFICATION_ID, mNotificationId),
- UserHandle(mUserContextTracker.userContext.userId)
- )
-
- val screenRecording = intent.getParcelableExtra(EXTRA_PATH, Uri::class.java)
- if (issueRecordingState.takeBugreport) {
- iActivityManager.requestBugReportWithExtraAttachment(screenRecording)
- } else {
- traceurMessageSender.shareTraces(applicationContext, screenRecording)
- }
- }
-
- dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
- panelInteractor.collapsePanels()
-
+ commandHandler.handleShareCommand(
+ intent.getIntExtra(EXTRA_NOTIFICATION_ID, mNotificationId),
+ intent.getParcelableExtra(EXTRA_PATH, Uri::class.java),
+ this
+ )
// Unlike all other actions, action_share has different behavior for the screen
// recording qs tile than it does for the record issue qs tile. Return sticky to
// avoid running any of the base class' code for this action.
@@ -135,8 +114,6 @@
companion object {
private const val TAG = "IssueRecordingService"
private const val CHANNEL_ID = "issue_record"
- private const val NOTIFY_SESSION_ENDED_SETTING = "should_notify_trace_session_ended"
- private const val DISABLED = 0
/**
* Get an intent to stop the issue recording service.
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandler.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandler.kt
new file mode 100644
index 0000000..32de0f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandler.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.recordissue
+
+import android.app.IActivityManager
+import android.app.NotificationManager
+import android.content.ContentResolver
+import android.content.Context
+import android.net.Uri
+import android.os.UserHandle
+import android.provider.Settings
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.settings.UserContextProvider
+import java.util.concurrent.Executor
+
+private const val NOTIFY_SESSION_ENDED_SETTING = "should_notify_trace_session_ended"
+private const val DISABLED = 0
+
+/**
+ * This class exists to unit test the business logic encapsulated in IssueRecordingService. Android
+ * specifically calls out that there is no supported way to test IntentServices here:
+ * https://developer.android.com/training/testing/other-components/services
+ */
+class IssueRecordingServiceCommandHandler(
+ private val bgExecutor: Executor,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+ private val panelInteractor: PanelInteractor,
+ private val traceurMessageSender: TraceurMessageSender,
+ private val issueRecordingState: IssueRecordingState,
+ private val iActivityManager: IActivityManager,
+ private val notificationManager: NotificationManager,
+ private val userContextProvider: UserContextProvider,
+) {
+
+ fun handleStartCommand() {
+ bgExecutor.execute { traceurMessageSender.startTracing(issueRecordingState.traceConfig) }
+ issueRecordingState.isRecording = true
+ }
+
+ fun handleStopCommand(contentResolver: ContentResolver) {
+ bgExecutor.execute {
+ if (issueRecordingState.traceConfig.longTrace) {
+ Settings.Global.putInt(contentResolver, NOTIFY_SESSION_ENDED_SETTING, DISABLED)
+ }
+ traceurMessageSender.stopTracing()
+ }
+ issueRecordingState.isRecording = false
+ }
+
+ fun handleShareCommand(notificationId: Int, screenRecording: Uri?, context: Context) {
+ bgExecutor.execute {
+ notificationManager.cancelAsUser(
+ null,
+ notificationId,
+ UserHandle(userContextProvider.userContext.userId)
+ )
+
+ if (issueRecordingState.takeBugreport) {
+ iActivityManager.requestBugReportWithExtraAttachment(screenRecording)
+ } else {
+ traceurMessageSender.shareTraces(context, screenRecording)
+ }
+ }
+
+ dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
+ panelInteractor.collapsePanels()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
index 907b92c..c092c2f 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
@@ -19,6 +19,7 @@
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
@@ -61,6 +62,7 @@
labelRes = R.string.qs_record_issue_label
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject FlashlightTile into tileViewModelMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
index 0589e6c6..c9712fc 100644
--- a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
@@ -19,6 +19,7 @@
import com.android.systemui.camera.CameraRotationModule
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileDataInteractor
@@ -42,8 +43,9 @@
@IntoMap
@StringKey(ROTATION_TILE_SPEC)
fun provideRotationAvailabilityInteractor(
- impl: RotationLockTileDataInteractor
+ impl: RotationLockTileDataInteractor
): QSTileAvailabilityInteractor
+
companion object {
private const val ROTATION_TILE_SPEC = "rotation"
@@ -60,6 +62,7 @@
labelRes = R.string.quick_settings_rotation_unlocked_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/** Inject Rotation tile into tileViewModelMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt b/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
index 4c730a0..7a57fba 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
@@ -16,8 +16,8 @@
package com.android.systemui.scene
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Module
import dagger.Provides
import dagger.multibindings.ElementsIntoSet
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index 6e89973..834db98 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.scene
+import androidx.compose.ui.unit.dp
import com.android.systemui.CoreStartable
import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
import com.android.systemui.scene.domain.SceneDomainModule
@@ -27,8 +28,11 @@
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.domain.startable.ScrimStartable
import com.android.systemui.scene.domain.startable.StatusBarStartable
+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.viewmodel.SplitEdgeDetector
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
import dagger.Binds
import dagger.Module
@@ -42,8 +46,10 @@
[
EmptySceneModule::class,
GoneSceneModule::class,
+ NotificationsShadeOverlayModule::class,
NotificationsShadeSceneModule::class,
NotificationsShadeSessionModule::class,
+ QuickSettingsShadeOverlayModule::class,
QuickSettingsSceneModule::class,
ShadeSceneModule::class,
SceneDomainModule::class,
@@ -99,6 +105,11 @@
Scenes.Shade.takeUnless { DualShade.isEnabled },
),
initialSceneKey = Scenes.Gone,
+ overlayKeys =
+ listOfNotNull(
+ Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
+ Overlays.QuickSettingsShade.takeIf { DualShade.isEnabled },
+ ),
navigationDistances =
mapOf(
Scenes.Gone to 0,
@@ -111,5 +122,15 @@
.mapValues { checkNotNull(it.value) }
)
}
+
+ @Provides
+ fun splitEdgeDetector(shadeInteractor: ShadeInteractor): SplitEdgeDetector {
+ return SplitEdgeDetector(
+ topEdgeSplitFraction = shadeInteractor::getTopEdgeSplitFraction,
+ // TODO(b/338577208): This should be 60dp at the top in the dual-shade UI. Better to
+ // replace this constant with dynamic window insets.
+ edgeSize = 40.dp
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 7d63b4c..a4c7d00 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -16,8 +16,8 @@
package com.android.systemui.scene
+import androidx.compose.ui.unit.dp
import com.android.systemui.CoreStartable
-import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule
import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
import com.android.systemui.scene.domain.SceneDomainModule
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
@@ -28,8 +28,11 @@
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.domain.startable.ScrimStartable
import com.android.systemui.scene.domain.startable.StatusBarStartable
+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.viewmodel.SplitEdgeDetector
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
import dagger.Binds
import dagger.Module
@@ -43,13 +46,14 @@
[
BouncerSceneModule::class,
CommunalSceneModule::class,
- ComposeBouncerFlagsModule::class,
EmptySceneModule::class,
GoneSceneModule::class,
LockscreenSceneModule::class,
QuickSettingsSceneModule::class,
ShadeSceneModule::class,
+ QuickSettingsShadeOverlayModule::class,
QuickSettingsShadeSceneModule::class,
+ NotificationsShadeOverlayModule::class,
NotificationsShadeSceneModule::class,
NotificationsShadeSessionModule::class,
SceneDomainModule::class,
@@ -108,6 +112,11 @@
Scenes.Shade.takeUnless { DualShade.isEnabled },
),
initialSceneKey = Scenes.Lockscreen,
+ overlayKeys =
+ listOfNotNull(
+ Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
+ Overlays.QuickSettingsShade.takeIf { DualShade.isEnabled },
+ ),
navigationDistances =
mapOf(
Scenes.Gone to 0,
@@ -123,5 +132,15 @@
.mapValues { checkNotNull(it.value) }
)
}
+
+ @Provides
+ fun splitEdgeDetector(shadeInteractor: ShadeInteractor): SplitEdgeDetector {
+ return SplitEdgeDetector(
+ topEdgeSplitFraction = shadeInteractor::getTopEdgeSplitFraction,
+ // TODO(b/338577208): This should be 60dp at the top in the dual-shade UI. Better to
+ // replace this constant with dynamic window insets.
+ edgeSize = 40.dp
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
index c176cca..2d40845 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
@@ -58,29 +58,20 @@
fun onSceneChange(from: SceneKey, to: SceneKey) {
check(from != to) { "from == to, from=${from.debugName}, to=${to.debugName}" }
- when (stackOperation(from, to)) {
- Clear -> {
- _backStack.value = sceneStackOf()
- }
- Push -> {
- _backStack.update { s -> s.push(from) }
- }
- Pop -> {
- _backStack.update { s ->
- checkNotNull(s.pop()) { "Cannot pop ${from.debugName} when stack is empty" }
- .also {
- val popped = s.peek()
- check(popped == to) {
- "Expected to pop ${to.debugName} but instead popped ${popped?.debugName}"
- }
- }
- }
+
+ _backStack.update { stack ->
+ when (stackOperation(from, to, stack)) {
+ null -> stack
+ Clear -> sceneStackOf()
+ Push -> stack.push(from)
+ Pop ->
+ checkNotNull(stack.pop()) { "Cannot pop ${from.debugName} when stack is empty" }
}
}
logger.logSceneBackStack(backStack.value.asIterable())
}
- private fun stackOperation(from: SceneKey, to: SceneKey): StackOperation {
+ private fun stackOperation(from: SceneKey, to: SceneKey, stack: SceneStack): StackOperation? {
val fromDistance =
checkNotNull(sceneContainerConfig.navigationDistances[from]) {
"No distance mapping for scene \"${from.debugName}\"!"
@@ -93,6 +84,7 @@
return when {
toDistance == 0 -> Clear
toDistance > fromDistance -> Push
+ stack.peek() != to -> null
toDistance < fromDistance -> Pop
else ->
error(
@@ -103,7 +95,10 @@
}
private sealed interface StackOperation
+
private data object Clear : StackOperation
+
private data object Push : StackOperation
+
private data object Pop : StackOperation
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index a2142b6..0d24adc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -202,8 +202,8 @@
}
is ObservableTransitionState.Transition -> {
when {
- transition.toScene == scene -> transition.progress
- transition.fromScene == scene -> transition.progress.map { 1f - it }
+ transition.toContent == scene -> transition.progress
+ transition.fromContent == scene -> transition.progress.map { 1f - it }
else -> flowOf(0f)
}
}
@@ -501,7 +501,7 @@
}
val inMidTransitionFromGone =
- (transitionState.value as? ObservableTransitionState.Transition)?.fromScene ==
+ (transitionState.value as? ObservableTransitionState.Transition)?.fromContent ==
Scenes.Gone
val isChangeAllowed =
to != Scenes.Gone ||
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 9c2b992..e477efe 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
@@ -25,6 +25,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -34,15 +35,19 @@
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
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
/** Business logic about the visibility of various parts of the window root view. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class WindowRootViewVisibilityInteractor
@Inject
@@ -73,22 +78,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.Lockscreen ||
+ Overlays.NotificationsShade in state.currentOverlays ||
+ Overlays.QuickSettingsShade in state.currentOverlays
+ )
is ObservableTransitionState.Transition ->
- state.toScene == Scenes.Shade ||
- state.toScene == Scenes.NotificationsShade ||
- state.toScene == Scenes.QuickSettingsShade ||
- state.toScene == Scenes.Lockscreen ||
- state.fromScene == Scenes.Shade ||
- state.fromScene == Scenes.NotificationsShade ||
- state.fromScene == Scenes.QuickSettingsShade ||
- state.fromScene == 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 == Overlays.NotificationsShade ||
+ state.toContent == Overlays.QuickSettingsShade ||
+ state.toContent == Scenes.Lockscreen ||
+ state.fromContent == Scenes.Shade ||
+ state.fromContent == Overlays.NotificationsShade ||
+ state.fromContent == Overlays.QuickSettingsShade ||
+ state.fromContent == Scenes.Lockscreen
+ )
+ }
}
}
.distinctUntilChanged()
@@ -101,10 +118,9 @@
* false if the device is asleep.
*/
val isLockscreenOrShadeVisibleAndInteractive: StateFlow<Boolean> =
- combine(
- isLockscreenOrShadeVisible,
- powerInteractor.isAwake,
- ) { isKeyguardAodOrShadeVisible, isAwake ->
+ combine(isLockscreenOrShadeVisible, powerInteractor.isAwake) {
+ isKeyguardAodOrShadeVisible,
+ isAwake ->
isKeyguardAodOrShadeVisible && isAwake
}
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt
index 99e554e..a313273 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt
@@ -21,7 +21,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import dagger.Binds
import dagger.Module
@@ -38,17 +38,17 @@
@Inject
constructor(
@Application applicationScope: CoroutineScope,
- shadeInteractor: ShadeInteractor,
+ shadeModeInteractor: ShadeModeInteractor,
) : SceneResolver {
override val targetFamily: SceneKey = SceneFamilies.NotifShade
override val resolvedScene: StateFlow<SceneKey> =
- shadeInteractor.shadeMode
+ shadeModeInteractor.shadeMode
.map(::notifShadeScene)
.stateIn(
applicationScope,
started = SharingStarted.Eagerly,
- initialValue = notifShadeScene(shadeInteractor.shadeMode.value),
+ initialValue = notifShadeScene(shadeModeInteractor.shadeMode.value),
)
override fun includesScene(scene: SceneKey): Boolean = scene in notifShadeScenes
@@ -61,11 +61,7 @@
}
companion object {
- val notifShadeScenes =
- setOf(
- Scenes.NotificationsShade,
- Scenes.Shade,
- )
+ val notifShadeScenes = setOf(Scenes.NotificationsShade, Scenes.Shade)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt
index 2962a3e..923e712a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt
@@ -21,7 +21,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import dagger.Binds
import dagger.Module
@@ -38,17 +38,17 @@
@Inject
constructor(
@Application applicationScope: CoroutineScope,
- shadeInteractor: ShadeInteractor,
+ shadeModeInteractor: ShadeModeInteractor,
) : SceneResolver {
override val targetFamily: SceneKey = SceneFamilies.QuickSettings
override val resolvedScene: StateFlow<SceneKey> =
- shadeInteractor.shadeMode
+ shadeModeInteractor.shadeMode
.map(::quickSettingsScene)
.stateIn(
applicationScope,
started = SharingStarted.Eagerly,
- initialValue = quickSettingsScene(shadeInteractor.shadeMode.value),
+ initialValue = quickSettingsScene(shadeModeInteractor.shadeMode.value),
)
override fun includesScene(scene: SceneKey): Boolean = scene in quickSettingsScenes
@@ -62,11 +62,7 @@
companion object {
val quickSettingsScenes =
- setOf(
- Scenes.QuickSettings,
- Scenes.QuickSettingsShade,
- Scenes.Shade,
- )
+ setOf(Scenes.QuickSettings, Scenes.QuickSettingsShade, Scenes.Shade)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 7eb48d6..98907b0 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -22,7 +22,9 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.AuthInteractionProperties
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -36,6 +38,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
@@ -62,6 +65,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
@@ -71,6 +75,8 @@
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.printSection
import com.android.systemui.util.println
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.MSDLPlayer
import dagger.Lazy
import java.io.PrintWriter
import java.util.Optional
@@ -78,6 +84,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
@@ -107,6 +114,7 @@
@Application private val applicationScope: CoroutineScope,
private val sceneInteractor: SceneInteractor,
private val deviceEntryInteractor: DeviceEntryInteractor,
+ private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
private val bouncerInteractor: BouncerInteractor,
private val keyguardInteractor: KeyguardInteractor,
@@ -134,10 +142,14 @@
private val dismissCallbackRegistry: DismissCallbackRegistry,
private val statusBarStateController: SysuiStatusBarStateController,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val vibratorHelper: VibratorHelper,
+ private val msdlPlayer: MSDLPlayer,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
+ private val authInteractionProperties = AuthInteractionProperties()
+
override fun start() {
if (SceneContainerFlag.isEnabled) {
sceneLogger.logFrameworkEnabled(isEnabled = true)
@@ -148,6 +160,7 @@
respondToFalsingDetections()
hydrateInteractionState()
handleBouncerOverscroll()
+ handleDeviceEntryHapticsWhileDeviceLocked()
hydrateWindowController()
hydrateBackStack()
resetShadeSessions()
@@ -189,7 +202,7 @@
// current scene
when (state) {
is ObservableTransitionState.Idle -> state.currentScene
- is ObservableTransitionState.Transition -> state.fromScene
+ is ObservableTransitionState.Transition -> state.fromContent
}.let { it == Scenes.Shade || it == Scenes.QuickSettings }
}
.distinctUntilChanged()
@@ -220,7 +233,7 @@
}
}
is ObservableTransitionState.Transition -> {
- if (state.fromScene == Scenes.Gone) {
+ if (state.fromContent == Scenes.Gone) {
true to "scene transitioning away from Gone"
} else {
null
@@ -351,8 +364,8 @@
is ObservableTransitionState.Idle -> setOf(transitionState.currentScene)
is ObservableTransitionState.Transition ->
setOf(
- transitionState.fromScene,
- transitionState.toScene,
+ transitionState.fromContent,
+ transitionState.toContent,
)
}
val isOnLockscreen = renderedScenes.contains(Scenes.Lockscreen)
@@ -461,7 +474,8 @@
sceneInteractor.transitionState.value as? ObservableTransitionState.Transition
?: return@collect
if (
- transition.fromScene == Scenes.Gone && transition.toScene == Scenes.Lockscreen
+ transition.fromContent == Scenes.Gone &&
+ transition.toContent == Scenes.Lockscreen
) {
switchToScene(
targetSceneKey = Scenes.Gone,
@@ -524,6 +538,51 @@
}
}
+ private fun handleDeviceEntryHapticsWhileDeviceLocked() {
+ applicationScope.launch {
+ deviceEntryInteractor.isDeviceEntered.collectLatest { isDeviceEntered ->
+ // Only check for haptics signals before device is entered
+ if (!isDeviceEntered) {
+ coroutineScope {
+ launch {
+ deviceEntryHapticsInteractor.playSuccessHaptic
+ .sample(sceneInteractor.currentScene)
+ .collect { currentScene ->
+ if (Flags.msdlFeedback()) {
+ msdlPlayer.playToken(
+ MSDLToken.UNLOCK,
+ authInteractionProperties,
+ )
+ } else {
+ vibratorHelper.vibrateAuthSuccess(
+ "$TAG, $currentScene device-entry::success"
+ )
+ }
+ }
+ }
+
+ launch {
+ deviceEntryHapticsInteractor.playErrorHaptic
+ .sample(sceneInteractor.currentScene)
+ .collect { currentScene ->
+ if (Flags.msdlFeedback()) {
+ msdlPlayer.playToken(
+ MSDLToken.FAILURE,
+ authInteractionProperties,
+ )
+ } else {
+ vibratorHelper.vibrateAuthError(
+ "$TAG, $currentScene device-entry::error"
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
/** Keeps [SysUiState] up-to-date */
private fun hydrateSystemUiState() {
applicationScope.launch {
@@ -570,15 +629,6 @@
}
applicationScope.launch {
- sceneInteractor.currentScene
- .map { it == Scenes.Bouncer }
- .distinctUntilChanged()
- .collect { isBouncerShowing ->
- windowController.setBouncerShowing(isBouncerShowing)
- }
- }
-
- applicationScope.launch {
occlusionInteractor.invisibleDueToOcclusion.collect { invisibleDueToOcclusion ->
windowController.setKeyguardOccluded(invisibleDueToOcclusion)
}
@@ -694,8 +744,8 @@
.filterIsInstance<ObservableTransitionState.Transition>()
// Only consider user-initiated (e.g. drags) that go from bouncer to lockscreen.
.filter { transition ->
- transition.fromScene == Scenes.Bouncer &&
- transition.toScene == Scenes.Lockscreen &&
+ transition.fromContent == Scenes.Bouncer &&
+ transition.toContent == Scenes.Lockscreen &&
transition.isInitiatedByUserInput
}
.flatMapLatest { it.progress }
@@ -816,4 +866,8 @@
.collectLatest { deviceEntryInteractor.refreshLockscreenEnabled() }
}
}
+
+ companion object {
+ private const val TAG = "SceneContainerStartable"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
index d1629c7..e352bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
@@ -19,7 +19,9 @@
package com.android.systemui.scene.domain.startable
import androidx.annotation.VisibleForTesting
+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.systemui.CoreStartable
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -33,6 +35,7 @@
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
import com.android.systemui.statusbar.phone.DozeServiceHost
@@ -74,6 +77,7 @@
deviceEntryInteractor.isDeviceEntered,
occlusionInteractor.invisibleDueToOcclusion,
sceneInteractor.currentScene,
+ sceneInteractor.currentOverlays,
sceneInteractor.transitionState,
keyguardInteractor.isDozing,
keyguardInteractor.isDreaming,
@@ -95,34 +99,29 @@
val isDeviceEntered = flowValues[0] as Boolean
val isOccluded = flowValues[1] as Boolean
val currentScene = flowValues[2] as SceneKey
- val transitionState = flowValues[3] as ObservableTransitionState
- val isDozing = flowValues[4] as Boolean
- val isDreaming = flowValues[5] as Boolean
- val biometricUnlockState = flowValues[6] as BiometricUnlockModel
- val isBrightnessMirrorVisible = flowValues[7] as Boolean
- val isPulsing = flowValues[8] as Boolean
- val hasPendingScreenOffCallback = flowValues[9] as Boolean
+ val currentOverlays = flowValues[3] as Set<OverlayKey>
+ val transitionState = flowValues[4] as ObservableTransitionState
+ val isDozing = flowValues[5] as Boolean
+ val isDreaming = flowValues[6] as Boolean
+ val biometricUnlockState = flowValues[7] as BiometricUnlockModel
+ val isBrightnessMirrorVisible = flowValues[8] as Boolean
+ val isPulsing = flowValues[9] as Boolean
+ val hasPendingScreenOffCallback = flowValues[10] as Boolean
// This is true when the lockscreen scene is either the current scene or somewhere
- // in the
- // navigation back stack of scenes.
+ // in the navigation back stack of scenes.
val isOnKeyguard = !isDeviceEntered
val isCurrentSceneBouncer = currentScene == Scenes.Bouncer
// This is true when moving away from one of the keyguard scenes to the gone scene.
- // It
- // happens only when unlocking or when dismissing a dismissible lockscreen.
+ // It happens only when unlocking or when dismissing a dismissible lockscreen.
val isTransitioningAwayFromKeyguard =
transitionState is ObservableTransitionState.Transition.ChangeScene &&
transitionState.fromScene.isKeyguard() &&
transitionState.toScene == Scenes.Gone
- // This is true when any of the shade scenes is the current scene.
- val isCurrentSceneShade = currentScene.isShade()
- // This is true when moving into one of the shade scenes when a non-shade scene.
- val isTransitioningToShade =
- transitionState is ObservableTransitionState.Transition.ChangeScene &&
- !transitionState.fromScene.isShade() &&
- transitionState.toScene.isShade()
+ // This is true when any of the shade scenes or overlays is the current content.
+ val isCurrentContentShade =
+ currentScene.isShade() || currentOverlays.any { it.isShade() }
// This is true after completing a transition to communal.
val isIdleOnCommunal = transitionState.isIdle(Scenes.Communal)
@@ -137,10 +136,10 @@
if (alternateBouncerInteractor.isVisibleState()) {
// This will cancel the keyguardFadingAway animation if it is running. We need
- // to do
- // this as otherwise it can remain pending and leave keyguard in a weird state.
+ // to do this as otherwise it can remain pending and leave keyguard in a weird
+ // state.
onKeyguardFadedAway(isTransitioningAwayFromKeyguard)
- if (!isTransitioningToShade) {
+ if (!transitionState.isTransitioningToShade()) {
// Safeguard which prevents the scrim from being stuck in the wrong
// state
Model(scrimState = ScrimState.KEYGUARD, unlocking = unlocking)
@@ -163,7 +162,7 @@
)
} else if (isBrightnessMirrorVisible) {
Model(scrimState = ScrimState.BRIGHTNESS_MIRROR, unlocking = unlocking)
- } else if (isCurrentSceneShade && !isDeviceEntered) {
+ } else if (isCurrentContentShade && !isDeviceEntered) {
Model(scrimState = ScrimState.SHADE_LOCKED, unlocking = unlocking)
} else if (isPulsing) {
Model(scrimState = ScrimState.PULSING, unlocking = unlocking)
@@ -171,8 +170,8 @@
Model(scrimState = ScrimState.OFF, unlocking = unlocking)
} else if (isDozing && !unlocking) {
// This will cancel the keyguardFadingAway animation if it is running. We need
- // to do
- // this as otherwise it can remain pending and leave keyguard in a weird state.
+ // to do this as otherwise it can remain pending and leave keyguard in a weird
+ // state.
onKeyguardFadedAway(isTransitioningAwayFromKeyguard)
Model(scrimState = ScrimState.AOD, unlocking = false)
} else if (isIdleOnCommunal) {
@@ -222,15 +221,24 @@
return this == Scenes.Lockscreen || this == Scenes.Bouncer
}
- private fun SceneKey.isShade(): Boolean {
+ private fun ContentKey.isShade(): Boolean {
return this == Scenes.Shade ||
this == Scenes.QuickSettings ||
- this == Scenes.NotificationsShade ||
- this == Scenes.QuickSettingsShade
+ this == Overlays.NotificationsShade ||
+ this == Overlays.QuickSettingsShade
}
- private data class Model(
- val scrimState: ScrimState,
- val unlocking: Boolean,
- )
+ private fun ObservableTransitionState.isTransitioningToShade(): Boolean {
+ return when (this) {
+ is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Transition.ChangeScene ->
+ !fromScene.isShade() && toScene.isShade()
+ is ObservableTransitionState.Transition.ReplaceOverlay ->
+ !fromOverlay.isShade() && toOverlay.isShade()
+ is ObservableTransitionState.Transition.ShowOrHideOverlay ->
+ !fromContent.isShade() && toContent.isShade()
+ }
+ }
+
+ private data class Model(val scrimState: ScrimState, val unlocking: Boolean)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index aa418e6..fb53ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -78,8 +78,8 @@
tag = TAG,
level = LogLevel.INFO,
messageInitializer = {
- str1 = transitionState.fromScene.toString()
- str2 = transitionState.toScene.toString()
+ str1 = transitionState.fromContent.toString()
+ str2 = transitionState.toContent.toString()
},
messagePrinter = { "Scene transition started: $str1 → $str2" },
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt
new file mode 100644
index 0000000..c47a850
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.scene.shared.model
+
+import com.android.compose.animation.scene.OverlayKey
+
+/**
+ * Keys of all known overlays.
+ *
+ * PLEASE KEEP THE KEYS SORTED ALPHABETICALLY.
+ */
+object Overlays {
+ /**
+ * The notifications shade overlay primarily shows a scrollable list of notifications.
+ *
+ * It's used only in the dual shade configuration, where there are two separate shades: one for
+ * notifications (this overlay) and another for [QuickSettingsShade].
+ *
+ * It's not used in the single/accordion configuration (swipe down once to reveal the shade,
+ * swipe down again the to expand quick settings) or in the "split" shade configuration (on
+ * large screens or unfolded foldables, where notifications and quick settings are shown
+ * side-by-side in their own columns).
+ */
+ @JvmField val NotificationsShade = OverlayKey("notifications_shade")
+
+ /**
+ * The quick settings shade overlay shows the quick settings tiles UI.
+ *
+ * It's used only in the dual shade configuration, where there are two separate shades: one for
+ * quick settings (this overlay) and another for [NotificationsShade].
+ *
+ * It's not used in the single/accordion configuration (swipe down once to reveal the shade,
+ * swipe down again the to expand quick settings) or in the "split" shade configuration (on
+ * large screens or unfolded foldables, where notifications and quick settings are shown
+ * side-by-side in their own columns).
+ */
+ @JvmField val QuickSettingsShade = OverlayKey("quick_settings_shade")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
deleted file mode 100644
index 8e2e8a1..0000000
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2023 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.scene.shared.model
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.lifecycle.Activatable
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Defines interface for classes that can describe a "scene".
- *
- * In the scene framework, there can be multiple scenes in a single scene "container". The container
- * takes care of rendering the current scene and allowing scenes to be switched from one to another
- * based on either user action (for example, swiping down while on the lock screen scene may switch
- * to the shade scene).
- */
-interface Scene : Activatable {
-
- /** Uniquely-identifying key for this scene. The key must be unique within its container. */
- val key: SceneKey
-
- /**
- * The mapping between [UserAction] and destination [UserActionResult]s.
- *
- * When the scene framework detects a user action, if the current scene has a map entry for that
- * user action, the framework starts a transition to the scene in the map.
- *
- * Once the [Scene] becomes the current one, the scene framework will read this property and set
- * up a collector to watch for new mapping values. If every map entry provided by the scene, the
- * framework will set up user input handling for its [UserAction] and, if such a user action is
- * detected, initiate a transition to the specified [UserActionResult].
- *
- * Note that reading from this method does _not_ mean that any user action has occurred.
- * Instead, the property is read before any user action/gesture is detected so that the
- * framework can decide whether to set up gesture/input detectors/listeners in case user actions
- * of the given types ever occur.
- *
- * Note that a missing value for a specific [UserAction] means that the user action of the given
- * type is not currently active in the scene and should be ignored by the framework, while the
- * current scene is this one.
- */
- val destinationScenes: Flow<Map<UserAction, UserActionResult>>
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
index ef5290f..115d664 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
@@ -54,7 +54,9 @@
* large screens or unfolded foldables, where notifications and quick settings are shown
* side-by-side in their own columns).
*/
- @JvmField val NotificationsShade = SceneKey("notifications_shade")
+ @Deprecated("The notifications shade scene has been replaced by an overlay")
+ @JvmField
+ val NotificationsShade = SceneKey("notifications_shade")
/**
* The quick settings scene shows the quick setting tiles.
@@ -83,7 +85,9 @@
* large screens or unfolded foldables, where notifications and quick settings are shown
* side-by-side in their own columns).
*/
- @JvmField val QuickSettingsShade = SceneKey("quick_settings_shade")
+ @Deprecated("The quick settings shade scene has been replaced by an overlay")
+ @JvmField
+ val QuickSettingsShade = SceneKey("quick_settings_shade")
/**
* The shade is the scene that shows a scrollable list of notifications and the minimized
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
index be95441..b9f57f2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
@@ -27,14 +27,6 @@
/** Reference to the gone/lockscreen to shade transition with split shade enabled. */
val ToSplitShade = TransitionKey("GoneToSplitShade")
- /** Reference to a scene transition that can collapse the shade scene instantly. */
- val CollapseShadeInstantly = TransitionKey("CollapseShadeInstantly")
-
- /**
- * Reference to a scene transition that brings up the shade from the bottom instead of the top.
- */
- val OpenBottomShade = TransitionKey("OpenBottomShade")
-
/**
* Reference to a scene transition that can collapse the shade scene slightly faster than a
* normal collapse would.
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index c1bb6fb..8a2e274 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -6,10 +6,10 @@
import android.view.View
import android.view.WindowInsets
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.shade.TouchLogger
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
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 ec6513a..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
@@ -44,11 +44,10 @@
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.ui.composable.ComposableScene
import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.composable.SceneContainer
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
@@ -78,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(
@@ -149,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,
@@ -187,8 +188,7 @@
) {
SceneContainer(
viewModel = viewModel,
- sceneByKey =
- sceneByKey.mapValues { (_, scene) -> scene as ComposableScene },
+ sceneByKey = sceneByKey,
overlayByKey = overlayByKey,
initialSceneKey = containerConfig.initialSceneKey,
dataSourceDelegator = dataSourceDelegator,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
deleted file mode 100644
index 88d4c4f..0000000
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
+++ /dev/null
@@ -1,91 +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.scene.ui.viewmodel
-
-import androidx.compose.ui.Alignment
-import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
-import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeMode
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.map
-
-class GoneSceneActionsViewModel
-@AssistedInject
-constructor(
- private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
-
- override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
- shadeInteractor.shadeMode
- .map { shadeMode ->
- buildMap<UserAction, UserActionResult> {
- if (
- shadeMode is ShadeMode.Single ||
- // TODO(b/338577208): Remove this once we add Dual Shade invocation
- // zones.
- shadeMode is ShadeMode.Dual
- ) {
- if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
- put(
- Swipe(
- pointerCount = 2,
- fromSource = Edge.Bottom,
- direction = SwipeDirection.Up,
- ),
- UserActionResult(SceneFamilies.QuickSettings, OpenBottomShade)
- )
- } else {
- put(
- Swipe(
- pointerCount = 2,
- fromSource = Edge.Top,
- direction = SwipeDirection.Down,
- ),
- UserActionResult(SceneFamilies.QuickSettings)
- )
- }
- }
-
- if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
- put(Swipe.Up, UserActionResult(SceneFamilies.NotifShade, OpenBottomShade))
- } else {
- put(
- Swipe.Down,
- UserActionResult(
- SceneFamilies.NotifShade,
- ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
- )
- )
- }
- }
- }
- .collect { setActions(it) }
- }
-
- @AssistedFactory
- interface Factory {
- fun create(): GoneSceneActionsViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModel.kt
new file mode 100644
index 0000000..7bf2b63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModel.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.scene.ui.viewmodel
+
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class GoneUserActionsViewModel
+@AssistedInject
+constructor(private val shadeInteractor: ShadeInteractor) : UserActionsViewModel() {
+
+ override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+ shadeInteractor.shadeMode.collect { shadeMode ->
+ setActions(
+ when (shadeMode) {
+ ShadeMode.Single -> fullscreenShadeActions()
+ ShadeMode.Split -> fullscreenShadeActions(transitionKey = ToSplitShade)
+ ShadeMode.Dual -> dualShadeActions()
+ }
+ )
+ }
+ }
+
+ private fun fullscreenShadeActions(
+ transitionKey: TransitionKey? = null
+ ): Map<UserAction, UserActionResult> {
+ return mapOf(
+ Swipe.Down to UserActionResult(Scenes.Shade, transitionKey),
+ Swipe(direction = SwipeDirection.Down, pointerCount = 2, fromSource = Edge.Top) to
+ UserActionResult(Scenes.Shade, transitionKey),
+ )
+ }
+
+ private fun dualShadeActions(): Map<UserAction, UserActionResult> {
+ return mapOf(
+ Swipe.Down to Overlays.NotificationsShade,
+ Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
+ Overlays.QuickSettingsShade,
+ )
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): GoneUserActionsViewModel
+ }
+}
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..5482394 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,8 +18,12 @@
import android.view.MotionEvent
import androidx.compose.runtime.getValue
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.DefaultEdgeDetector
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.SwipeSourceDetector
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.classifier.Classifier
@@ -30,12 +34,16 @@
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.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
/** Models UI state for the scene container. */
class SceneContainerViewModel
@@ -44,6 +52,8 @@
private val sceneInteractor: SceneInteractor,
private val falsingInteractor: FalsingInteractor,
private val powerInteractor: PowerInteractor,
+ private val shadeInteractor: ShadeInteractor,
+ private val splitEdgeDetector: SplitEdgeDetector,
private val logger: SceneLogger,
@Assisted private val motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
) : ExclusiveActivatable() {
@@ -56,6 +66,20 @@
/** Whether the container is visible. */
val isVisible: Boolean by hydrator.hydratedStateOf("isVisible", sceneInteractor.isVisible)
+ /**
+ * The [SwipeSourceDetector] to use for defining which edges of the screen can be defined in the
+ * [UserAction]s for this container.
+ */
+ val edgeDetector: SwipeSourceDetector by
+ hydrator.hydratedStateOf(
+ traceName = "edgeDetector",
+ initialValue = DefaultEdgeDetector,
+ source =
+ shadeInteractor.shadeMode.map {
+ if (it is ShadeMode.Dual) splitEdgeDetector else DefaultEdgeDetector
+ }
+ )
+
override suspend fun onActivated(): Nothing {
try {
// Sends a MotionEventHandler to the owner of the view-model so they can report
@@ -83,7 +107,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 +221,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/scene/ui/viewmodel/SplitEdgeDetector.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetector.kt
new file mode 100644
index 0000000..f88bcb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetector.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.scene.ui.viewmodel
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.FixedSizeEdgeDetector
+import com.android.compose.animation.scene.SwipeSource
+import com.android.compose.animation.scene.SwipeSourceDetector
+
+/**
+ * The edge of a [SceneContainer]. It differs from a standard [Edge] by splitting the top edge into
+ * top-left and top-right.
+ */
+enum class SceneContainerEdge(private val resolveEdge: (LayoutDirection) -> Resolved) :
+ SwipeSource {
+ TopLeft(resolveEdge = { Resolved.TopLeft }),
+ TopRight(resolveEdge = { Resolved.TopRight }),
+ TopStart(
+ resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.TopLeft else Resolved.TopRight }
+ ),
+ TopEnd(
+ resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.TopRight else Resolved.TopLeft }
+ ),
+ Bottom(resolveEdge = { Resolved.Bottom }),
+ Left(resolveEdge = { Resolved.Left }),
+ Right(resolveEdge = { Resolved.Right }),
+ Start(resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.Left else Resolved.Right }),
+ End(resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.Right else Resolved.Left });
+
+ override fun resolve(layoutDirection: LayoutDirection): Resolved {
+ return resolveEdge(layoutDirection)
+ }
+
+ enum class Resolved : SwipeSource.Resolved {
+ TopLeft,
+ TopRight,
+ Bottom,
+ Left,
+ Right,
+ }
+}
+
+/**
+ * A [SwipeSourceDetector] that detects edges similarly to [FixedSizeEdgeDetector], except that the
+ * top edge is split in two: top-left and top-right. The split point between the two is dynamic and
+ * may change during runtime.
+ *
+ * Callers who need to detect the start and end edges based on the layout direction (LTR vs RTL)
+ * should subscribe to [SceneContainerEdge.TopStart] and [SceneContainerEdge.TopEnd] instead. These
+ * will be resolved at runtime to [SceneContainerEdge.Resolved.TopLeft] and
+ * [SceneContainerEdge.Resolved.TopRight] appropriately. Similarly, [SceneContainerEdge.Start] and
+ * [SceneContainerEdge.End] will be resolved appropriately to [SceneContainerEdge.Resolved.Left] and
+ * [SceneContainerEdge.Resolved.Right].
+ *
+ * @param topEdgeSplitFraction A function which returns the fraction between [0..1] (i.e.,
+ * percentage) of screen width to consider the split point between "top-left" and "top-right"
+ * edges. It is called on each source detection event.
+ * @param edgeSize The fixed size of each edge.
+ */
+class SplitEdgeDetector(
+ val topEdgeSplitFraction: () -> Float,
+ val edgeSize: Dp,
+) : SwipeSourceDetector {
+
+ private val fixedEdgeDetector = FixedSizeEdgeDetector(edgeSize)
+
+ override fun source(
+ layoutSize: IntSize,
+ position: IntOffset,
+ density: Density,
+ orientation: Orientation,
+ ): SceneContainerEdge.Resolved? {
+ val fixedEdge =
+ fixedEdgeDetector.source(
+ layoutSize,
+ position,
+ density,
+ orientation,
+ )
+ return when (fixedEdge) {
+ Edge.Resolved.Top -> {
+ val topEdgeSplitFraction = topEdgeSplitFraction()
+ require(topEdgeSplitFraction in 0f..1f) {
+ "topEdgeSplitFraction must return a value between 0.0 and 1.0"
+ }
+ val isLeftSide = position.x < layoutSize.width * topEdgeSplitFraction
+ if (isLeftSide) SceneContainerEdge.Resolved.TopLeft
+ else SceneContainerEdge.Resolved.TopRight
+ }
+ Edge.Resolved.Left -> SceneContainerEdge.Resolved.Left
+ Edge.Resolved.Bottom -> SceneContainerEdge.Resolved.Bottom
+ Edge.Resolved.Right -> SceneContainerEdge.Resolved.Right
+ null -> null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModel.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModel.kt
index 368e4fa..57628d0 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModel.kt
@@ -25,14 +25,13 @@
import kotlinx.coroutines.flow.asStateFlow
/**
- * Base class for view-models that need to keep a map of scene actions (also known as "destination
- * scenes") up-to-date.
+ * Base class for view-models that need to keep a map of user actions up-to-date.
*
* Subclasses need only to override [hydrateActions], suspending forever if they need; they don't
* need to worry about resetting the value of [actions] when the view-model is deactivated/canceled,
* this base class takes care of it.
*/
-abstract class SceneActionsViewModel : ExclusiveActivatable() {
+abstract class UserActionsViewModel : ExclusiveActivatable() {
private val _actions = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap())
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
index a830e1b..9a9c576 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
@@ -21,6 +21,7 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.ScreenRecordTile
import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
@@ -74,6 +75,7 @@
labelRes = R.string.quick_settings_screen_record_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/** Inject ScreenRecord Tile into tileViewModelMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
index a77375c..f69b0cb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
@@ -19,7 +19,6 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
-import static com.android.systemui.Flags.screenshotSaveImageExporter;
import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
@@ -133,7 +132,6 @@
private final MessageContainerController mMessageContainerController;
private final AnnouncementResolver mAnnouncementResolver;
private Bitmap mScreenBitmap;
- private SaveImageInBackgroundTask mSaveInBgTask;
private boolean mScreenshotTakenInPortrait;
private boolean mAttachRequested;
private boolean mDetachRequested;
@@ -393,10 +391,6 @@
// Any cleanup needed when the service is being destroyed.
@Override
public void onDestroy() {
- if (mSaveInBgTask != null) {
- // just log success/failure for the pre-existing screenshot
- mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
- }
removeWindow();
releaseMediaPlayer();
releaseContext();
@@ -598,36 +592,12 @@
// Play the shutter sound to notify that we've taken a screenshot
playCameraSoundIfNeeded();
- if (screenshotSaveImageExporter()) {
- saveScreenshotInBackground(screenshot, UUID.randomUUID(), finisher, result -> {
- if (result.uri != null) {
- mScreenshotHandler.post(() -> Toast.makeText(mContext,
- R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
- }
- });
- } else {
- saveScreenshotInWorkerThread(
- screenshot.getUserHandle(),
- /* onComplete */ finisher,
- /* actionsReadyListener */ imageData -> {
- if (DEBUG_CALLBACK) {
- Log.d(TAG,
- "returning URI to finisher (Consumer<URI>): " + imageData.uri);
- }
- finisher.accept(imageData.uri);
- if (imageData.uri == null) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0,
- mPackageName);
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_save_text);
- } else {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName);
- mScreenshotHandler.post(() -> Toast.makeText(mContext,
- R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
- }
- },
- null);
- }
+ saveScreenshotInBackground(screenshot, UUID.randomUUID(), finisher, result -> {
+ if (result.uri != null) {
+ mScreenshotHandler.post(() -> Toast.makeText(mContext,
+ R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
+ }
+ });
}
/**
@@ -700,35 +670,6 @@
}
/**
- * Creates a new worker thread and saves the screenshot to the media store.
- */
- private void saveScreenshotInWorkerThread(
- UserHandle owner,
- @NonNull Consumer<Uri> finisher,
- @Nullable SaveImageInBackgroundTask.ActionsReadyListener actionsReadyListener,
- @Nullable SaveImageInBackgroundTask.QuickShareActionReadyListener
- quickShareActionsReadyListener) {
- SaveImageInBackgroundTask.SaveImageInBackgroundData
- data = new SaveImageInBackgroundTask.SaveImageInBackgroundData();
- data.image = mScreenBitmap;
- data.finisher = finisher;
- data.mActionsReadyListener = actionsReadyListener;
- data.mQuickShareActionsReadyListener = quickShareActionsReadyListener;
- data.owner = owner;
- data.displayId = mDisplay.getDisplayId();
-
- if (mSaveInBgTask != null) {
- // just log success/failure for the pre-existing screenshot
- mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
- }
-
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mFlags, mImageExporter,
- mScreenshotSmartActions, data,
- mScreenshotNotificationSmartActionsProvider);
- mSaveInBgTask.execute();
- }
-
- /**
* Logs success/failure of the screenshot saving task, and shows an error if it failed.
*/
private void logScreenshotResultStatus(Uri uri, UserHandle owner) {
@@ -745,13 +686,6 @@
}
}
- /**
- * Logs success/failure of the screenshot saving task, and shows an error if it failed.
- */
- private void logSuccessOnActionsReady(SaveImageInBackgroundTask.SavedImageData imageData) {
- logScreenshotResultStatus(imageData.uri, imageData.owner);
- }
-
private boolean isUserSetupComplete(UserHandle owner) {
return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0)
.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
index 474afa8b..56afb79 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
@@ -9,7 +9,6 @@
import android.view.ViewTreeObserver
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.constraintlayout.widget.Guideline
-import com.android.systemui.Flags.screenshotPrivateProfileBehaviorFix
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import com.android.systemui.screenshot.message.ProfileMessageController
@@ -49,44 +48,19 @@
}
fun onScreenshotTaken(screenshot: ScreenshotData) {
- if (screenshotPrivateProfileBehaviorFix()) {
- mainScope.launch {
- val profileData = profileMessageController.onScreenshotTaken(screenshot.userHandle)
- var notifiedApps: List<CharSequence> =
- screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
-
- // If profile first run needs to show, bias towards that, otherwise show screenshot
- // detection notification if needed.
- if (profileData != null) {
- workProfileFirstRunView.visibility = View.VISIBLE
- detectionNoticeView.visibility = View.GONE
- profileMessageController.bindView(workProfileFirstRunView, profileData) {
- animateOutMessageContainer()
- }
- animateInMessageContainer()
- } else if (notifiedApps.isNotEmpty()) {
- detectionNoticeView.visibility = View.VISIBLE
- workProfileFirstRunView.visibility = View.GONE
- screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
- animateInMessageContainer()
- }
- }
- } else {
- val workProfileData =
- workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
+ mainScope.launch {
+ val profileData = profileMessageController.onScreenshotTaken(screenshot.userHandle)
var notifiedApps: List<CharSequence> =
screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
- // If work profile first run needs to show, bias towards that, otherwise show screenshot
+ // If profile first run needs to show, bias towards that, otherwise show screenshot
// detection notification if needed.
- if (workProfileData != null) {
+ if (profileData != null) {
workProfileFirstRunView.visibility = View.VISIBLE
detectionNoticeView.visibility = View.GONE
- workProfileMessageController.populateView(
- workProfileFirstRunView,
- workProfileData,
- this::animateOutMessageContainer
- )
+ profileMessageController.bindView(workProfileFirstRunView, profileData) {
+ animateOutMessageContainer()
+ }
animateInMessageContainer()
} else if (notifiedApps.isNotEmpty()) {
detectionNoticeView.visibility = View.VISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
deleted file mode 100644
index 922997d..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2022 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.screenshot
-
-import android.util.Log
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-
-/** Implementation of [ScreenshotRequestProcessor] */
-class RequestProcessor(
- private val capture: ImageCapture,
- private val policy: ScreenshotPolicy,
-) : ScreenshotRequestProcessor {
-
- override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
- var result = screenshot
-
- // Apply work profile screenshots policy:
- //
- // If the focused app belongs to a work profile, transforms a full screen
- // (or partial) screenshot request to a task snapshot (provided image) screenshot.
-
- // Whenever displayContentInfo is fetched, the topComponent is also populated
- // regardless of the managed profile status.
-
- if (screenshot.type != TAKE_SCREENSHOT_PROVIDED_IMAGE) {
- val info = policy.findPrimaryContent(screenshot.displayId)
- Log.d(TAG, "findPrimaryContent: $info")
- result.taskId = info.taskId
- result.topComponent = info.component
- result.userHandle = info.user
-
- if (policy.isManagedProfile(info.user.identifier)) {
- val image =
- capture.captureTask(info.taskId)
- ?: throw RequestProcessorException("Task snapshot returned a null Bitmap!")
-
- // Provide the task snapshot as the screenshot
- result.type = TAKE_SCREENSHOT_PROVIDED_IMAGE
- result.bitmap = image
- result.screenBounds = info.bounds
- }
- }
-
- return result
- }
-}
-
-private const val TAG = "RequestProcessor"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
deleted file mode 100644
index 9bc3bd8..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright (C) 2019 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.screenshot;
-
-import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
-import static com.android.systemui.screenshot.LogConfig.DEBUG_STORAGE;
-import static com.android.systemui.screenshot.LogConfig.logTag;
-import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.UserHandle;
-import android.provider.DeviceConfig;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.systemui.flags.FeatureFlags;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Random;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-
-/**
- * An AsyncTask that saves an image to the media store in the background.
- */
-class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
- private static final String TAG = logTag(SaveImageInBackgroundTask.class);
-
- private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s";
- private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
-
- /**
- * POD used in the AsyncTask which saves an image in the background.
- */
- static class SaveImageInBackgroundData {
- public Bitmap image;
- public Consumer<Uri> finisher;
- public ActionsReadyListener mActionsReadyListener;
- public QuickShareActionReadyListener mQuickShareActionsReadyListener;
- public UserHandle owner;
- public int displayId;
-
- void clearImage() {
- image = null;
- }
- }
-
- /**
- * Structure returned by the SaveImageInBackgroundTask
- */
- public static class SavedImageData {
- public Uri uri;
- public List<Notification.Action> smartActions;
- public Notification.Action quickShareAction;
- public UserHandle owner;
- public String subject; // Title for sharing
- public Long imageTime; // Time at which screenshot was saved
-
- /**
- * Used to reset the return data on error
- */
- public void reset() {
- uri = null;
- smartActions = null;
- quickShareAction = null;
- subject = null;
- imageTime = null;
- }
- }
-
- /**
- * Structure returned by the QueryQuickShareInBackgroundTask
- */
- static class QuickShareData {
- public Notification.Action quickShareAction;
-
- /**
- * Used to reset the return data on error
- */
- public void reset() {
- quickShareAction = null;
- }
- }
-
- interface ActionsReadyListener {
- void onActionsReady(SavedImageData imageData);
- }
-
- interface QuickShareActionReadyListener {
- void onActionsReady(QuickShareData quickShareData);
- }
-
- private final Context mContext;
- private FeatureFlags mFlags;
- private final ScreenshotSmartActions mScreenshotSmartActions;
- private final SaveImageInBackgroundData mParams;
- private final SavedImageData mImageData;
- private final QuickShareData mQuickShareData;
-
- private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
- private String mScreenshotId;
- private final Random mRandom = new Random();
- private final ImageExporter mImageExporter;
- private long mImageTime;
-
- SaveImageInBackgroundTask(
- Context context,
- FeatureFlags flags,
- ImageExporter exporter,
- ScreenshotSmartActions screenshotSmartActions,
- SaveImageInBackgroundData data,
- ScreenshotNotificationSmartActionsProvider
- screenshotNotificationSmartActionsProvider
- ) {
- mContext = context;
- mFlags = flags;
- mScreenshotSmartActions = screenshotSmartActions;
- mImageData = new SavedImageData();
- mQuickShareData = new QuickShareData();
- mImageExporter = exporter;
-
- // Prepare all the output metadata
- mParams = data;
-
- // Initialize screenshot notification smart actions provider.
- mSmartActionsProvider = screenshotNotificationSmartActionsProvider;
- }
-
- @Override
- protected Void doInBackground(Void... paramsUnused) {
- if (isCancelled()) {
- if (DEBUG_STORAGE) {
- Log.d(TAG, "cancelled! returning null");
- }
- return null;
- }
- // TODO: move to constructor / from ScreenshotRequest
- final UUID requestId = UUID.randomUUID();
-
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
-
- Bitmap image = mParams.image;
- mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, requestId);
-
- boolean savingToOtherUser = mParams.owner != Process.myUserHandle();
- // Smart actions don't yet work for cross-user saves.
- boolean smartActionsEnabled = !savingToOtherUser
- && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS,
- true);
- try {
- if (smartActionsEnabled && mParams.mQuickShareActionsReadyListener != null) {
- // Since Quick Share target recommendation does not rely on image URL, it is
- // queried and surfaced before image compress/export. Action intent would not be
- // used, because it does not contain image URL.
- Notification.Action quickShare =
- queryQuickShareAction(mScreenshotId, image, mParams.owner, null);
- if (quickShare != null) {
- mQuickShareData.quickShareAction = quickShare;
- mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData);
- }
- }
-
- // Call synchronously here since already on a background thread.
- ListenableFuture<ImageExporter.Result> future =
- mImageExporter.export(Runnable::run, requestId, image, mParams.owner,
- mParams.displayId);
- ImageExporter.Result result = future.get();
- Log.d(TAG, "Saved screenshot: " + result);
- final Uri uri = result.uri;
- mImageTime = result.timestamp;
-
- CompletableFuture<List<Notification.Action>> smartActionsFuture =
- mScreenshotSmartActions.getSmartActionsFuture(
- mScreenshotId, uri, image, mSmartActionsProvider,
- ScreenshotSmartActionType.REGULAR_SMART_ACTIONS,
- smartActionsEnabled, mParams.owner);
- List<Notification.Action> smartActions = new ArrayList<>();
- if (smartActionsEnabled) {
- int timeoutMs = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
- 1000);
- smartActions.addAll(buildSmartActions(
- mScreenshotSmartActions.getSmartActions(
- mScreenshotId, smartActionsFuture, timeoutMs,
- mSmartActionsProvider,
- ScreenshotSmartActionType.REGULAR_SMART_ACTIONS),
- mContext));
- }
-
- mImageData.uri = uri;
- mImageData.owner = mParams.owner;
- mImageData.smartActions = smartActions;
- mImageData.quickShareAction = createQuickShareAction(
- mQuickShareData.quickShareAction, mScreenshotId, uri, mImageTime, image,
- mParams.owner);
- mImageData.subject = getSubjectString(mImageTime);
- mImageData.imageTime = mImageTime;
-
- mParams.mActionsReadyListener.onActionsReady(mImageData);
- if (DEBUG_CALLBACK) {
- Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) "
- + "finisher.accept(\"" + mImageData.uri + "\"");
- }
- mParams.finisher.accept(mImageData.uri);
- mParams.image = null;
- } catch (Exception e) {
- // IOException/UnsupportedOperationException may be thrown if external storage is
- // not mounted
- Log.d(TAG, "Failed to store screenshot", e);
- mParams.clearImage();
- mImageData.reset();
- mQuickShareData.reset();
- mParams.mActionsReadyListener.onActionsReady(mImageData);
- if (DEBUG_CALLBACK) {
- Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)");
- }
- mParams.finisher.accept(null);
- }
-
- return null;
- }
-
- /**
- * Update the listener run when the saving task completes. Used to avoid showing UI for the
- * first screenshot when a second one is taken.
- */
- void setActionsReadyListener(ActionsReadyListener listener) {
- mParams.mActionsReadyListener = listener;
- }
-
- @Override
- protected void onCancelled(Void params) {
- // If we are cancelled while the task is running in the background, we may get null
- // params. The finisher is expected to always be called back, so just use the baked-in
- // params from the ctor in any case.
- mImageData.reset();
- mQuickShareData.reset();
- mParams.mActionsReadyListener.onActionsReady(mImageData);
- if (DEBUG_CALLBACK) {
- Log.d(TAG, "onCancelled, calling (Consumer<Uri>) finisher.accept(null)");
- }
- mParams.finisher.accept(null);
- mParams.clearImage();
- }
-
- private List<Notification.Action> buildSmartActions(
- List<Notification.Action> actions, Context context) {
- List<Notification.Action> broadcastActions = new ArrayList<>();
- for (Notification.Action action : actions) {
- // Proxy smart actions through {@link SmartActionsReceiver} for logging smart actions.
- Bundle extras = action.getExtras();
- String actionType = extras.getString(
- ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
- ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
- Intent intent = new Intent(context, SmartActionsReceiver.class)
- .putExtra(SmartActionsReceiver.EXTRA_ACTION_INTENT, action.actionIntent)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- addIntentExtras(mScreenshotId, intent, actionType, true /* smartActionsEnabled */);
- PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
- mRandom.nextInt(),
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- broadcastActions.add(new Notification.Action.Builder(action.getIcon(), action.title,
- broadcastIntent).setContextual(true).addExtras(extras).build());
- }
- return broadcastActions;
- }
-
- private static void addIntentExtras(String screenshotId, Intent intent, String actionType,
- boolean smartActionsEnabled) {
- intent
- .putExtra(SmartActionsReceiver.EXTRA_ACTION_TYPE, actionType)
- .putExtra(SmartActionsReceiver.EXTRA_ID, screenshotId)
- .putExtra(SmartActionsReceiver.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
- }
-
- /**
- * Wrap the quickshare intent and populate the fillin intent with the URI
- */
- @VisibleForTesting
- Notification.Action createQuickShareAction(
- Notification.Action quickShare, String screenshotId, Uri uri, long imageTime,
- Bitmap image, UserHandle user) {
- if (quickShare == null) {
- return null;
- } else if (quickShare.actionIntent.isImmutable()) {
- Notification.Action quickShareWithUri =
- queryQuickShareAction(screenshotId, image, user, uri);
- if (quickShareWithUri == null
- || !quickShareWithUri.title.toString().contentEquals(quickShare.title)) {
- return null;
- }
- quickShare = quickShareWithUri;
- }
-
- Intent wrappedIntent = new Intent(mContext, SmartActionsReceiver.class)
- .putExtra(SmartActionsReceiver.EXTRA_ACTION_INTENT, quickShare.actionIntent)
- .putExtra(SmartActionsReceiver.EXTRA_ACTION_INTENT_FILLIN,
- createFillInIntent(uri, imageTime))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- Bundle extras = quickShare.getExtras();
- String actionType = extras.getString(
- ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
- ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
- // We only query for quick share actions when smart actions are enabled, so we can assert
- // that it's true here.
- addIntentExtras(screenshotId, wrappedIntent, actionType, true /* smartActionsEnabled */);
- PendingIntent broadcastIntent =
- PendingIntent.getBroadcast(mContext, mRandom.nextInt(), wrappedIntent,
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- return new Notification.Action.Builder(quickShare.getIcon(), quickShare.title,
- broadcastIntent)
- .setContextual(true)
- .addExtras(extras)
- .build();
- }
-
- private Intent createFillInIntent(Uri uri, long imageTime) {
- Intent fillIn = new Intent();
- fillIn.setType("image/png");
- fillIn.putExtra(Intent.EXTRA_STREAM, uri);
- fillIn.putExtra(Intent.EXTRA_SUBJECT, getSubjectString(imageTime));
- // Include URI in ClipData also, so that grantPermission picks it up.
- // We don't use setData here because some apps interpret this as "to:".
- ClipData clipData = new ClipData(
- new ClipDescription("content", new String[]{"image/png"}),
- new ClipData.Item(uri));
- fillIn.setClipData(clipData);
- fillIn.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return fillIn;
- }
-
- /**
- * Query and surface Quick Share chip if it is available. Action intent would not be used,
- * because it does not contain image URL which would be populated in {@link
- * #createQuickShareAction(Notification.Action, String, Uri, long, Bitmap, UserHandle)}
- */
-
- @VisibleForTesting
- Notification.Action queryQuickShareAction(
- String screenshotId, Bitmap image, UserHandle user, Uri uri) {
- CompletableFuture<List<Notification.Action>> quickShareActionsFuture =
- mScreenshotSmartActions.getSmartActionsFuture(
- screenshotId, uri, image, mSmartActionsProvider,
- ScreenshotSmartActionType.QUICK_SHARE_ACTION,
- true /* smartActionsEnabled */, user);
- int timeoutMs = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_QUICK_SHARE_ACTIONS_TIMEOUT_MS,
- 500);
- List<Notification.Action> quickShareActions =
- mScreenshotSmartActions.getSmartActions(
- screenshotId, quickShareActionsFuture, timeoutMs,
- mSmartActionsProvider,
- ScreenshotSmartActionType.QUICK_SHARE_ACTION);
- if (!quickShareActions.isEmpty()) {
- return quickShareActions.get(0);
- }
- return null;
- }
-
- private static String getSubjectString(long imageTime) {
- String subjectDate = DateFormat.getDateTimeInstance().format(new Date(imageTime));
- return String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 7b802a2..fe58bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -18,7 +18,6 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static com.android.systemui.Flags.screenshotSaveImageExporter;
import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
@@ -124,7 +123,6 @@
private final MessageContainerController mMessageContainerController;
private final AnnouncementResolver mAnnouncementResolver;
private Bitmap mScreenBitmap;
- private SaveImageInBackgroundTask mSaveInBgTask;
private boolean mScreenshotTakenInPortrait;
private Animator mScreenshotAnimation;
private RequestCallback mCurrentRequestCallback;
@@ -373,10 +371,6 @@
// Any cleanup needed when the service is being destroyed.
@Override
public void onDestroy() {
- if (mSaveInBgTask != null) {
- // just log success/failure for the pre-existing screenshot
- mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
- }
removeWindow();
releaseMediaPlayer();
releaseContext();
@@ -525,36 +519,12 @@
// Play the shutter sound to notify that we've taken a screenshot
playCameraSoundIfNeeded();
- if (screenshotSaveImageExporter()) {
- saveScreenshotInBackground(screenshot, UUID.randomUUID(), finisher, result -> {
- if (result.uri != null) {
- mScreenshotHandler.post(() -> Toast.makeText(mContext,
- R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
- }
- });
- } else {
- saveScreenshotInWorkerThread(
- screenshot.getUserHandle(),
- /* onComplete */ finisher,
- /* actionsReadyListener */ imageData -> {
- if (DEBUG_CALLBACK) {
- Log.d(TAG,
- "returning URI to finisher (Consumer<URI>): " + imageData.uri);
- }
- finisher.accept(imageData.uri);
- if (imageData.uri == null) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0,
- mPackageName);
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_save_text);
- } else {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName);
- mScreenshotHandler.post(() -> Toast.makeText(mContext,
- R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
- }
- },
- null);
- }
+ saveScreenshotInBackground(screenshot, UUID.randomUUID(), finisher, result -> {
+ if (result.uri != null) {
+ mScreenshotHandler.post(() -> Toast.makeText(mContext,
+ R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
+ }
+ });
}
/**
@@ -627,35 +597,6 @@
}
/**
- * Creates a new worker thread and saves the screenshot to the media store.
- */
- private void saveScreenshotInWorkerThread(
- UserHandle owner,
- @NonNull Consumer<Uri> finisher,
- @Nullable SaveImageInBackgroundTask.ActionsReadyListener actionsReadyListener,
- @Nullable SaveImageInBackgroundTask.QuickShareActionReadyListener
- quickShareActionsReadyListener) {
- SaveImageInBackgroundTask.SaveImageInBackgroundData
- data = new SaveImageInBackgroundTask.SaveImageInBackgroundData();
- data.image = mScreenBitmap;
- data.finisher = finisher;
- data.mActionsReadyListener = actionsReadyListener;
- data.mQuickShareActionsReadyListener = quickShareActionsReadyListener;
- data.owner = owner;
- data.displayId = mDisplay.getDisplayId();
-
- if (mSaveInBgTask != null) {
- // just log success/failure for the pre-existing screenshot
- mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
- }
-
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mFlags, mImageExporter,
- mScreenshotSmartActions, data,
- mScreenshotNotificationSmartActionsProvider);
- mSaveInBgTask.execute();
- }
-
- /**
* Logs success/failure of the screenshot saving task, and shows an error if it failed.
*/
private void logScreenshotResultStatus(Uri uri, UserHandle owner) {
@@ -672,13 +613,6 @@
}
}
- /**
- * Logs success/failure of the screenshot saving task, and shows an error if it failed.
- */
- private void logSuccessOnActionsReady(SaveImageInBackgroundTask.SavedImageData imageData) {
- logScreenshotResultStatus(imageData.uri, imageData.owner);
- }
-
private boolean isUserSetupComplete(UserHandle owner) {
return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0)
.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
index 3ad4075a..ee1008d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
@@ -27,5 +27,5 @@
suspend fun process(original: ScreenshotData): ScreenshotData
}
-/** Exception thrown by [RequestProcessor] if something goes wrong. */
+/** Exception thrown by [ScreenshotRequestProcessor] if something goes wrong. */
class RequestProcessorException(message: String) : IllegalStateException(message)
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 9db1f24..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,10 +68,14 @@
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;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
@@ -98,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;
@@ -114,6 +119,7 @@
private Button mCancel;
private CheckBox mBacklinksIncludeDataCheckBox;
private TextView mBacklinksDataTextView;
+ private TextView mBacklinksCrossProfileError;
private AppClipsViewModel mViewModel;
private ResultReceiver mResultReceiver;
@@ -190,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);
@@ -310,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,
@@ -344,10 +351,63 @@
// Set up the dropdown when multiple backlinks are available.
if (backlinksData.size() > 1) {
- setUpListPopupWindow(backlinksData, mBacklinksDataTextView);
+ setUpListPopupWindow(updateBacklinkLabelsWithDuplicateNames(backlinksData),
+ mBacklinksDataTextView);
}
}
+ /**
+ * If there are more than 1 backlinks that have the same app name, then this method appends
+ * a numerical suffix to such backlinks to help users distinguish.
+ */
+ private List<InternalBacklinksData> updateBacklinkLabelsWithDuplicateNames(
+ List<InternalBacklinksData> backlinksData) {
+ // Check if there are multiple backlinks with same name.
+ Map<String, Integer> duplicateNamedBacklinksCountMap = new HashMap<>();
+ for (InternalBacklinksData data : backlinksData) {
+ if (duplicateNamedBacklinksCountMap.containsKey(data.getDisplayLabel())) {
+ int duplicateCount = duplicateNamedBacklinksCountMap.get(data.getDisplayLabel());
+ if (duplicateCount == 0) {
+ // If this is the first time the loop is coming across a duplicate name, set the
+ // count to 2. This way the count starts from 1 for all duplicate named
+ // backlinks.
+ duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), 2);
+ } else {
+ // For all duplicate named backlinks, increase the duplicate count by 1.
+ duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), duplicateCount + 1);
+ }
+ } else {
+ // This is the first time the loop is coming across a backlink with this name. Set
+ // its count to 0. The loop will increase its count by 1 when a duplicate is found.
+ duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), 0);
+ }
+ }
+
+ // Go through the backlinks in reverse order as it is easier to assign the numerical suffix
+ // in descending order of frequency using the duplicate map that was built earlier. For
+ // example, if "App A" is present 3 times, then we assign display label "App A (3)" first
+ // and then "App A (2)", lastly "App A (1)".
+ for (InternalBacklinksData data : backlinksData.reversed()) {
+ String originalBacklinkLabel = data.getDisplayLabel();
+ int duplicateCount = duplicateNamedBacklinksCountMap.get(originalBacklinkLabel);
+
+ // The display label should only be updated if there are multiple backlinks with the
+ // same name.
+ if (duplicateCount > 0) {
+ // Update the display label to: "App name (count)"
+ data.setDisplayLabel(
+ getString(R.string.backlinks_duplicate_label_format, originalBacklinkLabel,
+ duplicateCount));
+
+ // Decrease the duplicate count and update the map.
+ duplicateCount--;
+ duplicateNamedBacklinksCountMap.put(originalBacklinkLabel, duplicateCount);
+ }
+ }
+
+ return backlinksData;
+ }
+
private void setUpListPopupWindow(List<InternalBacklinksData> backlinksData, View anchor) {
ListPopupWindow listPopupWindow = new ListPopupWindow(this);
listPopupWindow.setAnchorView(anchor);
@@ -365,7 +425,7 @@
public View getView(int position, @Nullable View convertView, ViewGroup parent) {
TextView itemView = (TextView) super.getView(position, convertView, parent);
InternalBacklinksData data = backlinksData.get(position);
- itemView.setText(data.getClipData().getDescription().getLabel());
+ itemView.setText(data.getDisplayLabel());
Drawable icon = data.getAppIcon();
icon.setBounds(createBacklinksTextViewDrawableBounds());
@@ -387,7 +447,7 @@
* expected to be already set when this method is called.
*/
private void updateBacklinksTextView(InternalBacklinksData backlinksData) {
- mBacklinksDataTextView.setText(backlinksData.getClipData().getDescription().getLabel());
+ mBacklinksDataTextView.setText(backlinksData.getDisplayLabel());
Drawable appIcon = backlinksData.getAppIcon();
Rect compoundDrawableBounds = createBacklinksTextViewDrawableBounds();
appIcon.setBounds(compoundDrawableBounds);
@@ -404,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..4b1504f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -23,14 +23,14 @@
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.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
@@ -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,9 @@
allTasksOnDisplay
.stream()
.filter(taskInfo -> shouldIncludeTask(taskInfo, taskIdsToIgnore))
+ .map(taskInfo -> new InternalTaskInfo(taskInfo.topActivityInfo,
+ taskInfo.taskId, taskInfo.userId,
+ getPackageManagerForUser(taskInfo.userId)))
.map(this::getBacklinksDataForTaskInfo)
.toList(),
mBgExecutor);
@@ -250,6 +256,17 @@
return Futures.transformAsync(backlinksNestedListFuture, Futures::allAsList, mBgExecutor);
}
+ 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();
+ }
+
/**
* Returns all tasks on a given display after querying {@link IActivityTaskManager} from the
* {@link #mBgExecutor}.
@@ -284,33 +301,23 @@
/**
* 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());
- }
-
- /**
- * Returns whether the app represented by the provided {@code packageName} can be launched
- * through the all apps tray by a user.
- */
- private boolean canAppStartThroughLauncher(String packageName) {
- // 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;
+ && taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_STANDARD;
}
/**
@@ -318,18 +325,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 +376,27 @@
* {@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);
- ClipData mainLauncherIntent = ClipData.newIntent(appName,
- getMainLauncherIntentForPackage(packageName));
- InternalBacklinksData fallback = new InternalBacklinksData(mainLauncherIntent, appIcon);
+ String screenshottedAppName = internalTaskInfo.getTopActivityAppName();
+ Drawable screenshottedAppIcon = internalTaskInfo.getTopActivityAppIcon();
+ Intent screenshottedAppMainLauncherIntent = getMainLauncherIntentForTask(
+ internalTaskInfo.getTopActivityPackageName(), internalTaskInfo.getPackageManager());
+ ClipData screenshottedAppMainLauncherClipData =
+ ClipData.newIntent(screenshottedAppName, screenshottedAppMainLauncherIntent);
+ InternalBacklinksData fallback =
+ new BacklinksData(screenshottedAppMainLauncherClipData, screenshottedAppIcon);
if (content == null) {
return fallback;
}
@@ -378,10 +408,14 @@
Uri uri = content.getWebUri();
Intent backlinksIntent = new Intent(ACTION_VIEW).setData(uri);
- if (doesIntentResolveToSamePackage(backlinksIntent, packageName)) {
+ BacklinkDisplayInfo backlinkDisplayInfo = getInfoThatResolvesIntent(backlinksIntent,
+ internalTaskInfo);
+ if (backlinkDisplayInfo != null) {
DebugLogger.INSTANCE.logcatMessage(this,
() -> "getBacklinksDataFromAssistContent: using app provided uri");
- return new InternalBacklinksData(ClipData.newRawUri(appName, uri), appIcon);
+ return new BacklinksData(
+ ClipData.newRawUri(backlinkDisplayInfo.getDisplayLabel(), uri),
+ backlinkDisplayInfo.getAppIcon());
}
}
@@ -391,11 +425,14 @@
() -> "getBacklinksDataFromAssistContent: app has provided an intent");
Intent backlinksIntent = content.getIntent();
- if (doesIntentResolveToSamePackage(backlinksIntent, packageName)) {
+ BacklinkDisplayInfo backlinkDisplayInfo = getInfoThatResolvesIntent(backlinksIntent,
+ internalTaskInfo);
+ if (backlinkDisplayInfo != null) {
DebugLogger.INSTANCE.logcatMessage(this,
() -> "getBacklinksDataFromAssistContent: using app provided intent");
- return new InternalBacklinksData(ClipData.newIntent(appName, backlinksIntent),
- appIcon);
+ return new BacklinksData(
+ ClipData.newIntent(backlinkDisplayInfo.getDisplayLabel(), backlinksIntent),
+ backlinkDisplayInfo.getAppIcon());
}
}
@@ -404,28 +441,77 @@
return fallback;
}
- private boolean doesIntentResolveToSamePackage(Intent intentToResolve,
- String requiredPackageName) {
- ComponentName resolvedComponent = intentToResolve.resolveActivity(mPackageManager);
- if (resolvedComponent == null) {
- return false;
+ /**
+ * Returns {@link BacklinkDisplayInfo} for the app that would resolve the provided backlink
+ * {@link Intent}.
+ *
+ * <p>The method uses the {@link PackageManager} available in the provided
+ * {@link InternalTaskInfo}.
+ *
+ * <p>This method returns {@code null} if Android is not able to resolve the backlink intent or
+ * if the resolved app does not have an icon in the launcher.
+ */
+ @Nullable
+ private BacklinkDisplayInfo getInfoThatResolvesIntent(Intent backlinkIntent,
+ InternalTaskInfo internalTaskInfo) {
+ PackageManager packageManager = internalTaskInfo.getPackageManager();
+
+ // Query for all available activities as there is a chance that multiple apps could resolve
+ // the intent. In such cases the normal `intent.resolveActivity` API returns the activity
+ // resolver info which isn't helpful for further checks. Also, using MATCH_DEFAULT_ONLY flag
+ // is required as that flag will be used when the notes app builds the intent and calls
+ // startActivity with the intent.
+ List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(backlinkIntent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (resolveInfos.isEmpty()) {
+ DebugLogger.INSTANCE.logcatMessage(this,
+ () -> "getInfoThatResolvesIntent: could not resolve backlink intent");
+ return null;
}
- return resolvedComponent.getPackageName().equals(requiredPackageName);
+ // Only use the first result as the list is ordered from best match to worst and Android
+ // will also use the best match with `intent.startActivity` API which notes app will use.
+ ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
+ if (activityInfo == null) {
+ DebugLogger.INSTANCE.logcatMessage(this,
+ () -> "getInfoThatResolvesIntent: could not find activity info for backlink "
+ + "intent");
+ return null;
+ }
+
+ // Ignore resolved backlink app if users cannot start it through all apps tray.
+ if (!canAppStartThroughLauncher(activityInfo.packageName, packageManager)) {
+ DebugLogger.INSTANCE.logcatMessage(this,
+ () -> "getInfoThatResolvesIntent: ignoring resolved backlink app as it cannot"
+ + " start through launcher");
+ return null;
+ }
+
+ Drawable appIcon = InternalBacklinksDataKt.getAppIcon(activityInfo, packageManager);
+ String appName = InternalBacklinksDataKt.getAppName(activityInfo, packageManager);
+ return new BacklinkDisplayInfo(appIcon, appName);
}
- private String getAppNameOfTask(TaskInfo taskInfo) {
- return taskInfo.topActivityInfo.loadLabel(mPackageManager).toString();
+ /**
+ * Returns whether the app represented by the provided {@code pkgName} can be launched through
+ * the all apps tray by the user.
+ */
+ private static boolean canAppStartThroughLauncher(String pkgName, PackageManager pkgManager) {
+ // Use Intent.resolveActivity API to check if the intent resolves as that is what Android
+ // uses internally when apps use Context.startActivity.
+ return getMainLauncherIntentForTask(pkgName, pkgManager)
+ .resolveActivity(pkgManager) != null;
}
- private Intent getMainLauncherIntentForPackage(String pkgName) {
+ private static Intent getMainLauncherIntentForTask(String pkgName,
+ PackageManager packageManager) {
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);
+ ResolveInfo resolvedActivity = packageManager.resolveActivity(intent, /* flags= */ 0);
if (resolvedActivity != null) {
intent.setComponent(resolvedActivity.getComponentInfo().getComponentName());
}
@@ -440,7 +526,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 +535,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 +555,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 0e312f9..f4faa36 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
@@ -16,8 +16,65 @@
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)
+/**
+ * A class to hold the [ClipData] for backlinks, the corresponding app's [Drawable] icon, and
+ * represent error when necessary.
+ */
+internal sealed class InternalBacklinksData(
+ // Fields from this object are made accessible through accessors to keep call sites simpler.
+ private val backlinkDisplayInfo: BacklinkDisplayInfo,
+) {
+ // Use separate field to access display label so that callers don't have to access through
+ // internal object.
+ var displayLabel: String
+ get() = backlinkDisplayInfo.displayLabel
+ set(value) {
+ backlinkDisplayInfo.displayLabel = value
+ }
+
+ // Use separate field to access app icon so that callers don't have to access through internal
+ // object.
+ val appIcon: Drawable
+ get() = backlinkDisplayInfo.appIcon
+
+ data class BacklinksData(val clipData: ClipData, private val icon: Drawable) :
+ InternalBacklinksData(BacklinkDisplayInfo(icon, clipData.description.label.toString()))
+
+ data class CrossProfileError(private val icon: Drawable, private var label: String) :
+ InternalBacklinksData(BacklinkDisplayInfo(icon, label))
+}
+
+/**
+ * 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
+) {
+ val topActivityNameForDebugLogging: String = topActivityInfo.name
+ val topActivityPackageName: String = topActivityInfo.packageName
+ val topActivityAppName: String by lazy { topActivityInfo.getAppName(packageManager) }
+ val topActivityAppIcon: Drawable by lazy { topActivityInfo.loadIcon(packageManager) }
+}
+
+internal fun ActivityInfo.getAppName(packageManager: PackageManager) =
+ loadLabel(packageManager).toString()
+
+internal fun ActivityInfo.getAppIcon(packageManager: PackageManager) = loadIcon(packageManager)
+
+/** A class to hold data that is used for displaying backlink to user in SysUI activity. */
+internal data class BacklinkDisplayInfo(val appIcon: Drawable, var displayLabel: String)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
index 44f767a..2cb9fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
@@ -19,14 +19,11 @@
import android.content.ComponentName
import android.content.Context
import android.os.Process
-import com.android.systemui.Flags.screenshotPrivateProfileBehaviorFix
import com.android.systemui.SystemUIService
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.screenshot.ImageCapture
-import com.android.systemui.screenshot.RequestProcessor
-import com.android.systemui.screenshot.ScreenshotPolicy
import com.android.systemui.screenshot.ScreenshotRequestProcessor
import com.android.systemui.screenshot.data.repository.DisplayContentRepository
import com.android.systemui.screenshot.data.repository.DisplayContentRepositoryImpl
@@ -68,23 +65,18 @@
@Application context: Context,
@Background background: CoroutineDispatcher,
imageCapture: ImageCapture,
- policyProvider: Provider<ScreenshotPolicy>,
- displayContentRepoProvider: Provider<DisplayContentRepository>,
+ displayContentRepo: DisplayContentRepository,
policyListProvider: Provider<List<CapturePolicy>>,
): ScreenshotRequestProcessor {
- return if (screenshotPrivateProfileBehaviorFix()) {
- PolicyRequestProcessor(
- background = background,
- capture = imageCapture,
- displayTasks = displayContentRepoProvider.get(),
- policies = policyListProvider.get(),
- defaultOwner = Process.myUserHandle(),
- defaultComponent =
- ComponentName(context.packageName, SystemUIService::class.java.toString())
- )
- } else {
- RequestProcessor(imageCapture, policyProvider.get())
- }
+ return PolicyRequestProcessor(
+ background = background,
+ capture = imageCapture,
+ displayTasks = displayContentRepo,
+ policies = policyListProvider.get(),
+ defaultOwner = Process.myUserHandle(),
+ defaultComponent =
+ ComponentName(context.packageName, SystemUIService::class.java.toString())
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
index e7ee961..edff4bf 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
@@ -17,6 +17,7 @@
package com.android.systemui.settings
import android.view.Display
+import com.android.systemui.util.annotations.WeaklyReferencedCallback
import java.util.concurrent.Executor
/**
@@ -52,6 +53,7 @@
fun getDisplay(displayId: Int): Display
/** Ćallback for notifying of changes. */
+ @WeaklyReferencedCallback
interface Callback {
/** Notifies that a display has been added. */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
index 05f19ef..b9f9b92 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
@@ -16,7 +16,6 @@
package com.android.systemui.settings;
-import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -67,7 +66,7 @@
@Background CoroutineDispatcher backgroundDispatcher,
@Background Handler handler
) {
- int startingUser = ActivityManager.getCurrentUser();
+ int startingUser = userManager.getBootUser().getIdentifier();
UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager,
iActivityManager, dumpManager, appScope, backgroundDispatcher, handler);
tracker.initialize(startingUser);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index ed590c3..553d1f5 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -103,11 +103,8 @@
override val userContentResolver: ContentResolver
get() = userContext.contentResolver
- override val userInfo: UserInfo
- get() {
- val user = userId
- return userProfiles.first { it.id == user }
- }
+ override var userInfo: UserInfo by SynchronizedDelegate(UserInfo(context.userId, "", 0))
+ protected set
/**
* Returns a [List<UserInfo>] of all profiles associated with the current user.
@@ -187,6 +184,7 @@
userHandle = handle
userContext = ctx
userProfiles = profiles.map { UserInfo(it) }
+ userInfo = profiles.first { it.id == user }
}
return ctx to profiles
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 083cee7..75165cb 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -16,8 +16,6 @@
package com.android.systemui.settings.brightness;
-import static com.android.systemui.Flags.hapticBrightnessSlider;
-
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
@@ -317,9 +315,7 @@
SeekbarHapticPlugin plugin = new SeekbarHapticPlugin(
mVibratorHelper,
mSystemClock);
- if (hapticBrightnessSlider()) {
- HapticSliderViewBinder.bind(viewRoot, plugin);
- }
+ HapticSliderViewBinder.bind(viewRoot, plugin);
return new BrightnessSliderController(
root, mFalsingManager, mUiEventLogger, plugin, mActivityStarter);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 3bb494b..7fa9926 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -96,7 +96,7 @@
private val lockscreenSmartspaceController: LockscreenSmartspaceController,
@CommunalTouchLog logBuffer: LogBuffer,
) : LifecycleOwner {
- private val logger = Logger(logBuffer, "GlanceableHubContainerController")
+ private val logger = Logger(logBuffer, TAG)
private class CommunalWrapper(context: Context) : FrameLayout(context) {
private val consumers: MutableSet<Consumer<Boolean>> = ArraySet()
@@ -301,7 +301,7 @@
if (touchMonitor == null) {
touchMonitor =
- ambientTouchComponentFactory.create(this, HashSet()).getTouchMonitor().apply {
+ ambientTouchComponentFactory.create(this, HashSet(), TAG).getTouchMonitor().apply {
init()
}
}
@@ -508,6 +508,11 @@
fun onTouchEvent(ev: MotionEvent): Boolean {
SceneContainerFlag.assertInLegacyMode()
+ if (communalContainerView == null) {
+ // Return early so we don't log unnecessarily and fill up our LogBuffer.
+ return false
+ }
+
// In the case that we are handling full swipes on the lockscreen, are on the lockscreen,
// and the touch is within the horizontal notification band on the screen, do not process
// the touch.
@@ -528,7 +533,7 @@
return false
}
- return communalContainerView?.let { handleTouchEventOnCommunalView(ev) } ?: false
+ return handleTouchEventOnCommunalView(ev)
}
private fun handleTouchEventOnCommunalView(ev: MotionEvent): Boolean {
@@ -630,4 +635,8 @@
override val lifecycle: Lifecycle
get() = lifecycleRegistry
+
+ companion object {
+ private const val TAG = "GlanceableHubContainer"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 7e0454c..3f3ad13 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -49,6 +49,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
@@ -342,6 +343,12 @@
this::setKeyguardOccluded
);
}
+ if (ComposeBouncerFlags.INSTANCE.isComposeBouncerOrSceneContainerEnabled()) {
+ collectFlow(mWindowRootView, mNotificationShadeWindowModel.isBouncerShowing(),
+ this::setBouncerShowing);
+ collectFlow(mWindowRootView, mNotificationShadeWindowModel.getDoesBouncerRequireIme(),
+ this::setKeyguardNeedsInput);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 16aef65..4ed4af6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -42,6 +42,7 @@
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -463,6 +464,9 @@
mJavaAdapter.alwaysCollectFlow(
mCommunalTransitionViewModelLazy.get().isUmoOnCommunal(),
this::setShouldUpdateSquishinessOnMedia);
+ mJavaAdapter.alwaysCollectFlow(
+ mShadeInteractor.isAnyExpanded(),
+ this::onAnyExpandedChanged);
}
private void initNotificationStackScrollLayoutController() {
@@ -482,6 +486,10 @@
}
}
+ private void onAnyExpandedChanged(boolean isAnyExpanded) {
+ mQsFrame.setVisibility(isAnyExpanded ? View.VISIBLE : View.INVISIBLE);
+ }
+
private void onNotificationScrolled(int newScrollPosition) {
updateExpansionEnabledAmbient();
}
@@ -1005,7 +1013,7 @@
// When expanding QS, let's authenticate the user if possible,
// this will speed up notification actions.
if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
- mDeviceEntryFaceAuthInteractor.onQsExpansionStared();
+ mDeviceEntryFaceAuthInteractor.onShadeExpansionStarted();
}
}
@@ -1063,13 +1071,17 @@
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
setClippingBounds();
- if (mSplitShadeEnabled) {
- // In split shade we want to pretend that QS are always collapsed so their behaviour and
- // interactions don't influence notifications as they do in portrait. But we want to set
- // 0 explicitly in case we're rotating from non-split shade with QS expansion of 1.
- mNotificationStackScrollLayoutController.setQsExpansionFraction(0);
- } else {
- mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+ if (!SceneContainerFlag.isEnabled()) {
+ if (mSplitShadeEnabled) {
+ // In split shade we want to pretend that QS are always collapsed so their
+ // behaviour and interactions don't influence notifications as they do in portrait.
+ // But we want to set 0 explicitly in case we're rotating from non-split shade with
+ // QS expansion of 1.
+ mNotificationStackScrollLayoutController.setQsExpansionFraction(0);
+ } else {
+ mNotificationStackScrollLayoutController.setQsExpansionFraction(
+ qsExpansionFraction);
+ }
}
mDepthController.setQsPanelExpansion(qsExpansionFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 23e2620..5d03a28 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade
import android.view.MotionEvent
-import androidx.compose.ui.Alignment
import com.android.systemui.assist.AssistManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -25,7 +24,6 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -177,7 +175,6 @@
sceneInteractor.changeScene(
SceneFamilies.NotifShade,
"ShadeController.animateExpandShade",
- OpenBottomShade.takeIf { shadeInteractor.shadeAlignment == Alignment.BottomEnd }
)
}
@@ -185,7 +182,6 @@
sceneInteractor.changeScene(
SceneFamilies.QuickSettings,
"ShadeController.animateExpandQs",
- OpenBottomShade.takeIf { shadeInteractor.shadeAlignment == Alignment.BottomEnd }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
index 7425807..99ff946 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -28,6 +28,8 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractorEmptyImpl
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractorEmptyImpl
import dagger.Binds
import dagger.Module
@@ -75,4 +77,8 @@
@Binds
@SysUISingleton
abstract fun bindsPrivacyChipRepository(impl: PrivacyChipRepositoryImpl): PrivacyChipRepository
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindShadeModeInteractor(impl: ShadeModeInteractorEmptyImpl): ShadeModeInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index da2024b..2348a11 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -41,6 +41,8 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractorSceneContainerImpl
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractorImpl
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -54,7 +56,7 @@
@SysUISingleton
fun provideBaseShadeInteractor(
sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
- sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
+ sceneContainerOff: Provider<ShadeInteractorLegacyImpl>,
): BaseShadeInteractor {
return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
@@ -67,7 +69,7 @@
@SysUISingleton
fun provideShadeController(
sceneContainerOn: Provider<ShadeControllerSceneImpl>,
- sceneContainerOff: Provider<ShadeControllerImpl>
+ sceneContainerOff: Provider<ShadeControllerImpl>,
): ShadeController {
return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
@@ -80,7 +82,7 @@
@SysUISingleton
fun provideShadeAnimationInteractor(
sceneContainerOn: Provider<ShadeAnimationInteractorSceneContainerImpl>,
- sceneContainerOff: Provider<ShadeAnimationInteractorLegacyImpl>
+ sceneContainerOff: Provider<ShadeAnimationInteractorLegacyImpl>,
): ShadeAnimationInteractor {
return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
@@ -93,7 +95,7 @@
@SysUISingleton
fun provideShadeBackActionInteractor(
sceneContainerOn: Provider<ShadeBackActionInteractorImpl>,
- sceneContainerOff: Provider<NotificationPanelViewController>
+ sceneContainerOff: Provider<NotificationPanelViewController>,
): ShadeBackActionInteractor {
return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
@@ -106,7 +108,7 @@
@SysUISingleton
fun provideShadeLockscreenInteractor(
sceneContainerOn: Provider<ShadeLockscreenInteractorImpl>,
- sceneContainerOff: Provider<NotificationPanelViewController>
+ sceneContainerOff: Provider<NotificationPanelViewController>,
): ShadeLockscreenInteractor {
return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
@@ -119,7 +121,7 @@
@SysUISingleton
fun providePanelExpansionInteractor(
sceneContainerOn: Provider<PanelExpansionInteractorImpl>,
- sceneContainerOff: Provider<NotificationPanelViewController>
+ sceneContainerOff: Provider<NotificationPanelViewController>,
): PanelExpansionInteractor {
return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
@@ -170,4 +172,8 @@
@Binds
@SysUISingleton
abstract fun bindsPrivacyChipRepository(impl: PrivacyChipRepositoryImpl): PrivacyChipRepository
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindShadeModeInteractor(impl: ShadeModeInteractorImpl): ShadeModeInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 018144b..fc8a593 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -37,10 +37,10 @@
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.view.SceneWindowRootView
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index a4fed873..193056c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -18,7 +18,6 @@
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -103,9 +102,6 @@
@Deprecated("Use ShadeInteractor.isQsBypassingShade instead")
val legacyExpandImmediate: StateFlow<Boolean>
- /** Whether dual shade should be aligned to the bottom (true) or to the top (false). */
- val isDualShadeAlignedToBottom: Boolean
-
/**
* Whether the shade layout should be wide (true) or narrow (false).
*
@@ -238,9 +234,6 @@
private val _isShadeLayoutWide = MutableStateFlow(false)
override val isShadeLayoutWide: StateFlow<Boolean> = _isShadeLayoutWide.asStateFlow()
- override val isDualShadeAlignedToBottom =
- applicationContext.resources.getBoolean(R.bool.config_dualShadeAlignedToBottom)
-
override fun setShadeLayoutWide(isShadeLayoutWide: Boolean) {
_isShadeLayoutWide.value = isShadeLayoutWide
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
index 79a94a5..8467185 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
@@ -49,10 +49,10 @@
is ObservableTransitionState.Idle -> flowOf(false)
is ObservableTransitionState.Transition ->
if (
- (state.fromScene == Scenes.Shade &&
- state.toScene != Scenes.QuickSettings) ||
- (state.fromScene == Scenes.QuickSettings &&
- state.toScene != Scenes.Shade)
+ (state.fromContent == Scenes.Shade &&
+ state.toContent != Scenes.QuickSettings) ||
+ (state.fromContent == Scenes.QuickSettings &&
+ state.toContent != Scenes.Shade)
) {
state.isUserInputOngoing.map { !it }
} else {
@@ -71,10 +71,10 @@
is ObservableTransitionState.Transition ->
if (
state.isInitiatedByUserInput &&
- (state.fromScene == Scenes.Shade ||
- state.toScene == Scenes.Shade ||
- state.fromScene == Scenes.QuickSettings ||
- state.toScene == Scenes.QuickSettings)
+ (state.fromContent == Scenes.Shade ||
+ state.toContent == Scenes.Shade ||
+ state.fromContent == Scenes.QuickSettings ||
+ state.toContent == Scenes.QuickSettings)
) {
state.isUserInputOngoing.map { !it }
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 45f359e..6fb96da 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -16,7 +16,7 @@
package com.android.systemui.shade.domain.interactor
-import com.android.systemui.shade.shared.model.ShadeAlignment
+import androidx.annotation.FloatRange
import com.android.systemui.shade.shared.model.ShadeMode
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -71,8 +71,19 @@
*/
val isShadeLayoutWide: StateFlow<Boolean>
- /** How to align the shade content. */
- val shadeAlignment: ShadeAlignment
+ /**
+ * The fraction between [0..1] (i.e., percentage) of screen width to consider the threshold
+ * between "top-left" and "top-right" for the purposes of dual-shade invocation.
+ *
+ * When the dual-shade is not wide, this always returns 0.5 (the top edge is evenly split). On
+ * wide layouts however, a larger fraction is returned because only the area of the system
+ * status icons is considered top-right.
+ *
+ * Note that this fraction only determines the split between the absolute left and right
+ * directions. In RTL layouts, the "top-start" edge will resolve to "top-right", and "top-end"
+ * will resolve to "top-left".
+ */
+ @FloatRange(from = 0.0, to = 1.0) fun getTopEdgeSplitFraction(): Float
}
/** ShadeInteractor methods with implementations that differ between non-empty impls. */
@@ -134,7 +145,7 @@
fun createAnyExpansionFlow(
scope: CoroutineScope,
shadeExpansion: Flow<Float>,
- qsExpansion: Flow<Float>
+ qsExpansion: Flow<Float>,
): StateFlow<Float> {
return combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
.stateIn(scope, SharingStarted.Eagerly, 0f)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
index e77aca9..6c0b55a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -48,5 +47,6 @@
override val isExpandToQsEnabled: Flow<Boolean> = inactiveFlowBoolean
override val shadeMode: StateFlow<ShadeMode> = MutableStateFlow(ShadeMode.Single)
override val isShadeLayoutWide: StateFlow<Boolean> = inactiveFlowBoolean
- override val shadeAlignment: ShadeAlignment = ShadeAlignment.Top
+
+ override fun getTopEdgeSplitFraction(): Float = 0.5f
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index d64b21f..3eab02a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -24,10 +24,6 @@
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
-import com.android.systemui.shade.shared.model.ShadeAlignment
-import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
@@ -55,11 +51,14 @@
keyguardRepository: KeyguardRepository,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
powerInteractor: PowerInteractor,
- private val shadeRepository: ShadeRepository,
userSetupRepository: UserSetupRepository,
userSwitcherInteractor: UserSwitcherInteractor,
private val baseShadeInteractor: BaseShadeInteractor,
-) : ShadeInteractor, BaseShadeInteractor by baseShadeInteractor {
+ shadeModeInteractor: ShadeModeInteractor,
+) :
+ ShadeInteractor,
+ BaseShadeInteractor by baseShadeInteractor,
+ ShadeModeInteractor by shadeModeInteractor {
override val isShadeEnabled: StateFlow<Boolean> =
disableFlagsRepository.disableFlags
.map { it.isShadeEnabled() }
@@ -103,26 +102,6 @@
}
}
- override val isShadeLayoutWide: StateFlow<Boolean> = shadeRepository.isShadeLayoutWide
-
- override val shadeMode: StateFlow<ShadeMode> =
- isShadeLayoutWide
- .map(this::determineShadeMode)
- .stateIn(
- scope,
- SharingStarted.Eagerly,
- initialValue = determineShadeMode(isShadeLayoutWide.value)
- )
-
- override val shadeAlignment: ShadeAlignment
- get() {
- return if (shadeRepository.isDualShadeAlignedToBottom) {
- ShadeAlignment.Bottom
- } else {
- ShadeAlignment.Top
- }
- }
-
override val isExpandToQsEnabled: Flow<Boolean> =
combine(
disableFlagsRepository.disableFlags,
@@ -139,12 +118,4 @@
disableFlags.isQuickSettingsEnabled() &&
!isDozing
}
-
- private fun determineShadeMode(isShadeLayoutWide: Boolean): ShadeMode {
- return when {
- DualShade.isEnabled -> ShadeMode.Dual
- isShadeLayoutWide -> ShadeMode.Split
- else -> ShadeMode.Single
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 6a21531..e84cfa5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -90,8 +90,8 @@
when (state) {
is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- state.toScene == quickSettingsScene &&
- state.fromScene != notificationsScene
+ state.toContent == quickSettingsScene &&
+ state.fromContent != notificationsScene
}
}
.distinctUntilChanged()
@@ -99,14 +99,17 @@
.distinctUntilChanged()
override val isQsFullscreen: Flow<Boolean> =
- sceneInteractor
- .resolveSceneFamily(SceneFamilies.QuickSettings)
- .flatMapLatestConflated { quickSettingsScene ->
+ combine(
+ shadeRepository.isShadeLayoutWide,
+ sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
+ ::Pair
+ )
+ .flatMapLatestConflated { (isShadeLayoutWide, quickSettingsScene) ->
sceneInteractor.transitionState
.map { state ->
when (state) {
is ObservableTransitionState.Idle ->
- state.currentScene == quickSettingsScene
+ !isShadeLayoutWide && state.currentScene == quickSettingsScene
is ObservableTransitionState.Transition -> false
}
}
@@ -147,9 +150,9 @@
flowOf(0f)
}
is ObservableTransitionState.Transition ->
- if (state.toScene == resolvedSceneKey) {
+ if (state.toContent == resolvedSceneKey) {
state.progress
- } else if (state.fromScene == resolvedSceneKey) {
+ } else if (state.fromContent == resolvedSceneKey) {
state.progress.map { progress -> 1 - progress }
} else {
flowOf(0f)
@@ -172,8 +175,8 @@
is ObservableTransitionState.Transition ->
sceneInteractor.resolveSceneFamily(sceneKey).map { resolvedSceneKey ->
state.isInitiatedByUserInput &&
- (state.toScene == resolvedSceneKey ||
- state.fromScene == resolvedSceneKey)
+ (state.toContent == resolvedSceneKey ||
+ state.fromContent == resolvedSceneKey)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
new file mode 100644
index 0000000..77ae679
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.shade.domain.interactor
+
+import androidx.annotation.FloatRange
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Defines interface for classes that can provide state and business logic related to the mode of
+ * the shade.
+ */
+interface ShadeModeInteractor {
+
+ /**
+ * The version of the shade layout to use.
+ *
+ * Note: Most likely, you want to read [isShadeLayoutWide] instead of this.
+ */
+ val shadeMode: StateFlow<ShadeMode>
+
+ /**
+ * Whether the shade layout should be wide (true) or narrow (false).
+ *
+ * In a wide layout, notifications and quick settings each take up only half the screen width
+ * (whether they are shown at the same time or not). In a narrow layout, they can each be as
+ * wide as the entire screen.
+ */
+ val isShadeLayoutWide: StateFlow<Boolean>
+
+ /**
+ * The fraction between [0..1] (i.e., percentage) of screen width to consider the threshold
+ * between "top-left" and "top-right" for the purposes of dual-shade invocation.
+ *
+ * When the dual-shade is not wide, this always returns 0.5 (the top edge is evenly split). On
+ * wide layouts however, a larger fraction is returned because only the area of the system
+ * status icons is considered top-right.
+ *
+ * Note that this fraction only determines the split between the absolute left and right
+ * directions. In RTL layouts, the "top-start" edge will resolve to "top-right", and "top-end"
+ * will resolve to "top-left".
+ */
+ @FloatRange(from = 0.0, to = 1.0) fun getTopEdgeSplitFraction(): Float
+}
+
+class ShadeModeInteractorImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val repository: ShadeRepository,
+) : ShadeModeInteractor {
+
+ override val isShadeLayoutWide: StateFlow<Boolean> = repository.isShadeLayoutWide
+
+ override val shadeMode: StateFlow<ShadeMode> =
+ isShadeLayoutWide
+ .map(this::determineShadeMode)
+ .stateIn(
+ applicationScope,
+ SharingStarted.Eagerly,
+ initialValue = determineShadeMode(isShadeLayoutWide.value),
+ )
+
+ @FloatRange(from = 0.0, to = 1.0)
+ override fun getTopEdgeSplitFraction(): Float {
+ // Note: this implicitly relies on isShadeLayoutWide being hot (i.e. collected). This
+ // assumption allows us to query its value on demand (during swipe source detection) instead
+ // of running another infinite coroutine.
+ // TODO(b/338577208): Instead of being fixed at 0.8f, this should dynamically updated based
+ // on the position of system-status icons in the status bar.
+ return if (repository.isShadeLayoutWide.value) 0.8f else 0.5f
+ }
+
+ private fun determineShadeMode(isShadeLayoutWide: Boolean): ShadeMode {
+ return when {
+ DualShade.isEnabled -> ShadeMode.Dual
+ isShadeLayoutWide -> ShadeMode.Split
+ else -> ShadeMode.Single
+ }
+ }
+}
+
+class ShadeModeInteractorEmptyImpl @Inject constructor() : ShadeModeInteractor {
+
+ override val shadeMode: StateFlow<ShadeMode> = MutableStateFlow(ShadeMode.Single)
+
+ override val isShadeLayoutWide: StateFlow<Boolean> = MutableStateFlow(false)
+
+ override fun getTopEdgeSplitFraction(): Float = 0.5f
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
index 9c4bf1f..9655d92 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
@@ -16,16 +16,25 @@
package com.android.systemui.shade.ui.viewmodel
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.BooleanFlowOperators.any
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
/** Models UI state for the shade window. */
@@ -34,6 +43,9 @@
@Inject
constructor(
keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ sceneInteractor: dagger.Lazy<SceneInteractor>,
+ authenticationInteractor: dagger.Lazy<AuthenticationInteractor>,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
) {
/**
* Considered to be occluded if in OCCLUDED, DREAMING, GLANCEABLE_HUB/Communal, or transitioning
@@ -70,4 +82,53 @@
),
)
.any()
+
+ /**
+ * Whether bouncer is currently showing or not.
+ *
+ * Applicable only when either [SceneContainerFlag] or [ComposeBouncerFlags] are enabled,
+ * otherwise it throws an error.
+ */
+ val isBouncerShowing: Flow<Boolean> =
+ when {
+ SceneContainerFlag.isEnabled -> {
+ sceneInteractor.get().transitionState.map { it.isIdle(Scenes.Bouncer) }
+ }
+ ComposeBouncerFlags.isOnlyComposeBouncerEnabled() -> primaryBouncerInteractor.isShowing
+ else ->
+ flow {
+ error(
+ "Consume this flow only when SceneContainerFlag " +
+ "or ComposeBouncerFlags are enabled"
+ )
+ }
+ }.distinctUntilChanged()
+
+ /**
+ * Whether the bouncer currently require IME for device entry.
+ *
+ * This emits true when the authentication method is set to password and the bouncer is
+ * currently showing. Throws an error when this is used without either [SceneContainerFlag] or
+ * [ComposeBouncerFlags]
+ */
+ val doesBouncerRequireIme: Flow<Boolean> =
+ if (ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
+ // This is required to make the window, where the bouncer resides,
+ // focusable. InputMethodManager allows IME to be shown only for views
+ // in windows that do not have the FLAG_NOT_FOCUSABLE flag.
+
+ isBouncerShowing
+ .sample(authenticationInteractor.get().authenticationMethod, ::Pair)
+ .map { (showing, authMethod) ->
+ showing && authMethod == AuthenticationMethodModel.Password
+ }
+ } else {
+ flow {
+ error(
+ "Consume this flow only when SceneContainerFlag " +
+ "or ComposeBouncerFlags are enabled"
+ )
+ }
+ }
+ .distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
deleted file mode 100644
index abf1f4c..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
+++ /dev/null
@@ -1,68 +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.shade.ui.viewmodel
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-/**
- * Models UI state and handles user input for the overlay shade UI, which shows a shade as an
- * overlay on top of another scene UI.
- */
-class OverlayShadeViewModel
-@AssistedInject
-constructor(
- private val sceneInteractor: SceneInteractor,
- shadeInteractor: ShadeInteractor,
-) : ExclusiveActivatable() {
- private val _backgroundScene = MutableStateFlow(Scenes.Lockscreen)
- /** The scene to show in the background when the overlay shade is open. */
- val backgroundScene: StateFlow<SceneKey> = _backgroundScene.asStateFlow()
-
- /** Dictates the alignment of the overlay shade panel on the screen. */
- val panelAlignment = shadeInteractor.shadeAlignment
-
- override suspend fun onActivated(): Nothing {
- sceneInteractor.resolveSceneFamily(SceneFamilies.Home).collect { sceneKey ->
- _backgroundScene.value = sceneKey
- }
- awaitCancellation()
- }
-
- /** Notifies that the user has clicked the semi-transparent background scrim. */
- fun onScrimClicked() {
- sceneInteractor.changeScene(
- toScene = SceneFamilies.Home,
- loggingReason = "Shade scrim clicked",
- )
- }
-
- @AssistedFactory
- interface Factory {
- fun create(): OverlayShadeViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
index 7c70759..ce4c081 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
@@ -43,7 +43,7 @@
/**
* Models UI state used to render the content of the shade scene.
*
- * Different from [ShadeSceneActionsViewModel], which only models user actions that can be performed
+ * Different from [ShadeUserActionsViewModel], which only models user actions that can be performed
* to navigate to other scenes.
*/
class ShadeSceneContentViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
index ab71913..f8a850a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
@@ -24,7 +24,7 @@
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import dagger.assisted.AssistedFactory
@@ -36,12 +36,12 @@
*
* Different from the [ShadeSceneContentViewModel] which models the _content_ of the scene.
*/
-class ShadeSceneActionsViewModel
+class ShadeUserActionsViewModel
@AssistedInject
constructor(
private val qsSceneAdapter: QSSceneAdapter,
private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
+) : UserActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
combine(
@@ -71,6 +71,6 @@
@AssistedFactory
interface Factory {
- fun create(): ShadeSceneActionsViewModel
+ fun create(): ShadeUserActionsViewModel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
index 922560f..0e1bf72 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -16,6 +16,7 @@
package com.android.systemui.smartspace.config
+import com.android.systemui.Flags.smartspaceViewpager2
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
@@ -23,4 +24,7 @@
BcSmartspaceConfigPlugin {
override val isDefaultDateWeatherDisabled: Boolean
get() = true
+
+ override val isViewPager2Enabled: Boolean
+ get() = smartspaceViewpager2()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f88fd7d..862f33bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -63,6 +63,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.annotations.KeepForWeakReference;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBar;
@@ -192,10 +193,10 @@
private static final String SHOW_IME_SWITCHER_KEY = "showImeSwitcherKey";
private final Object mLock = new Object();
- private ArrayList<Callbacks> mCallbacks = new ArrayList<>();
- private Handler mHandler = new H(Looper.getMainLooper());
+ private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
+ private final Handler mHandler = new H(Looper.getMainLooper());
/** A map of display id - disable flag pair */
- private SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
+ private final SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
/**
* The last ID of the display where IME window for which we received setImeWindowStatus
* event.
@@ -207,6 +208,21 @@
private final @Nullable DumpHandler mDumpHandler;
private final @Nullable Lazy<PowerInteractor> mPowerInteractor;
+ @KeepForWeakReference
+ private final DisplayTracker.Callback mDisplayTrackerCallback = new DisplayTracker.Callback() {
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mDisplayDisabled.remove(displayId);
+ }
+ // This callback is registered with {@link #mHandler} that already posts to run on
+ // main thread, so it is safe to dispatch directly.
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ mCallbacks.get(i).onDisplayRemoved(displayId);
+ }
+ }
+ };
+
/**
* These methods are called back on the main thread.
*/
@@ -576,19 +592,8 @@
mDisplayTracker = displayTracker;
mRegistry = registry;
mDumpHandler = dumpHandler;
- mDisplayTracker.addDisplayChangeCallback(new DisplayTracker.Callback() {
- @Override
- public void onDisplayRemoved(int displayId) {
- synchronized (mLock) {
- mDisplayDisabled.remove(displayId);
- }
- // This callback is registered with {@link #mHandler} that already posts to run on
- // main thread, so it is safe to dispatch directly.
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).onDisplayRemoved(displayId);
- }
- }
- }, new HandlerExecutor(mHandler));
+ mDisplayTracker.addDisplayChangeCallback(mDisplayTrackerCallback,
+ new HandlerExecutor(mHandler));
// We always have default display.
setDisabled(mDisplayTracker.getDefaultDisplayId(), DISABLE_NONE, DISABLE2_NONE);
mPowerInteractor = powerInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 2b9daef..5ef5a7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -79,6 +79,7 @@
import com.android.app.viewcapture.ViewCapture;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.res.R;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -107,6 +108,7 @@
private Context mDisplayContext;
private final Context mSysUiContext;
private final Handler mHandler = new H(Looper.getMainLooper());
+ private final Handler mBackgroundHandler;
private long mShowDelayMs = 0L;
private final IBinder mWindowToken = new Binder();
private final CommandQueue mCommandQueue;
@@ -139,7 +141,8 @@
@Inject
public ImmersiveModeConfirmation(Context context, CommandQueue commandQueue,
SecureSettings secureSettings,
- dagger.Lazy<ViewCapture> daggerLazyViewCapture) {
+ dagger.Lazy<ViewCapture> daggerLazyViewCapture,
+ @Background Handler backgroundHandler) {
mSysUiContext = context;
final Display display = mSysUiContext.getDisplay();
mDisplayContext = display.getDisplayId() == DEFAULT_DISPLAY
@@ -147,6 +150,7 @@
mCommandQueue = commandQueue;
mSecureSettings = secureSettings;
mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
+ mBackgroundHandler = backgroundHandler;
}
boolean loadSetting(int currentUserId) {
@@ -329,7 +333,7 @@
}
}
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
- mContentObserver = new ContentObserver(mHandler) {
+ mContentObserver = new ContentObserver(mBackgroundHandler) {
@Override
public void onChange(boolean selfChange) {
onSettingChanged(mSysUiContext.getUserId());
@@ -343,6 +347,9 @@
mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.USER_SETUP_COMPLETE, mContentObserver,
UserHandle.USER_CURRENT);
+ mBackgroundHandler.post(() -> {
+ loadSetting(UserHandle.USER_CURRENT);
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 696e222..d523bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -255,8 +255,8 @@
}
final float stackBottom = SceneContainerFlag.isEnabled()
- ? ambientState.getStackTop() + ambientState.getStackHeight()
- : ambientState.getStackY() + ambientState.getStackHeight();
+ ? ambientState.getStackTop() + ambientState.getInterpolatedStackHeight()
+ : ambientState.getStackY() + ambientState.getInterpolatedStackHeight();
if (viewState.hidden) {
// if the shelf is hidden, position it at the end of the stack (plus the clip
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 6eadd26..87f360e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -58,6 +58,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIcon.Shape;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Flags;
import com.android.systemui.res.R;
@@ -211,16 +212,19 @@
/** Should always be preceded by {@link #reloadDimens()} */
@VisibleForTesting
public void maybeUpdateIconScaleDimens() {
- // We do not resize and scale system icons (on the right), only notification icons (on the
- // left).
- if (isNotification()) {
- updateIconScaleForNotifications();
+ // We scale notification icons (on the left) plus icons on the right that explicitly
+ // want FIXED_SPACE.
+ boolean useNonSystemIconScaling = isNotification()
+ || (usesModeIcons() && mIcon != null && mIcon.shape == Shape.FIXED_SPACE);
+
+ if (useNonSystemIconScaling) {
+ updateIconScaleForNonSystemIcons();
} else {
updateIconScaleForSystemIcons();
}
}
- private void updateIconScaleForNotifications() {
+ private void updateIconScaleForNonSystemIcons() {
float iconScale;
// we need to scale the image size to be same as the original size
// (fit mOriginalStatusBarIconSize), then we can scale it with mScaleToFitNewIconSize
@@ -411,7 +415,9 @@
if (!levelEquals) {
setImageLevel(icon.iconLevel);
}
-
+ if (usesModeIcons() && icon.shape == Shape.FIXED_SPACE) {
+ setScaleType(ScaleType.FIT_CENTER);
+ }
if (!visibilityEquals) {
setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
}
@@ -501,7 +507,12 @@
@Nullable
private Drawable loadDrawable(Context context, StatusBarIcon statusBarIcon) {
if (usesModeIcons() && statusBarIcon.preloadedIcon != null) {
- return statusBarIcon.preloadedIcon.mutate();
+ Drawable.ConstantState cached = statusBarIcon.preloadedIcon.getConstantState();
+ if (cached != null) {
+ return cached.newDrawable(mContext.getResources()).mutate();
+ } else {
+ return statusBarIcon.preloadedIcon.mutate();
+ }
} else {
int userId = statusBarIcon.user.getIdentifier();
if (userId == UserHandle.USER_ALL) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 64dfc6c..735b4c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -54,6 +54,8 @@
VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+ private static final VibrationAttributes COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST);
private final Executor mExecutor;
@@ -151,7 +153,7 @@
vibrate(Process.myUid(),
"com.android.systemui",
BIOMETRIC_SUCCESS_VIBRATION_EFFECT, reason,
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES);
}
/**
@@ -160,7 +162,7 @@
public void vibrateAuthError(String reason) {
vibrate(Process.myUid(), "com.android.systemui",
BIOMETRIC_ERROR_VIBRATION_EFFECT, reason,
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
index 84ccaec..cce9a16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
@@ -42,9 +42,9 @@
*
* Example adb commands:
*
- * To show a chip with the SysUI icon and custom text:
+ * To show a chip with the SysUI icon and custom text and color:
* ```
- * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min
+ * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343"
* ```
*
* To hide the chip:
@@ -87,6 +87,17 @@
valueParser = Type.String,
)
+ private val backgroundColor: Int? by
+ param(
+ longName = "color",
+ shortName = "c",
+ description =
+ "The color to show as the chip background color. " +
+ "You can either just write a basic color like 'red' or 'green', " +
+ "or you can include a #RRGGBB string in this format: \"\\\\#434343\".",
+ valueParser = Type.Color,
+ )
+
private val hide by
flag(
longName = "hide",
@@ -119,21 +130,26 @@
return
}
+ val colors =
+ if (backgroundColor != null) {
+ ColorsModel.Custom(backgroundColorInt = backgroundColor!!)
+ } else {
+ ColorsModel.Themed
+ }
+
val currentText = text
if (currentText != null) {
_chip.value =
OngoingActivityChipModel.Shown.Text(
icon = appIcon,
- // TODO(b/361346412): Include a demo with a custom color theme.
- colors = ColorsModel.Themed,
+ colors = colors,
text = currentText,
)
} else {
_chip.value =
OngoingActivityChipModel.Shown.Timer(
icon = appIcon,
- // TODO(b/361346412): Include a demo with a custom color theme.
- colors = ColorsModel.Themed,
+ colors = colors,
startTimeMs = systemClock.elapsedRealtime(),
onClickListener = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt
new file mode 100644
index 0000000..4c0c461
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.statusbar.chips.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the status bar ron chips flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarRonChips {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.statusBarRonChips()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is not enabled to ensure that the refactor author catches issues in testing.
+ * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+ */
+ @JvmStatic
+ inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
new file mode 100644
index 0000000..3b1e565
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -0,0 +1,314 @@
+/*
+ * 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.statusbar.chips.ui.binder
+
+import android.annotation.IdRes
+import android.content.res.ColorStateList
+import android.graphics.drawable.GradientDrawable
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.systemui.common.ui.binder.IconViewBinder
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+
+/** Binder for ongoing activity chip views. */
+object OngoingActivityChipBinder {
+ /** Binds the given [chipModel] data to the given [chipView]. */
+ fun bind(chipModel: OngoingActivityChipModel, chipView: View) {
+ val chipContext = chipView.context
+ val chipDefaultIconView: ImageView =
+ chipView.requireViewById(R.id.ongoing_activity_chip_icon)
+ val chipTimeView: ChipChronometer =
+ chipView.requireViewById(R.id.ongoing_activity_chip_time)
+ val chipTextView: TextView = chipView.requireViewById(R.id.ongoing_activity_chip_text)
+ val chipBackgroundView: ChipBackgroundContainer =
+ chipView.requireViewById(R.id.ongoing_activity_chip_background)
+
+ when (chipModel) {
+ is OngoingActivityChipModel.Shown -> {
+ // Data
+ setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
+ setChipMainContent(chipModel, chipTextView, chipTimeView)
+ chipView.setOnClickListener(chipModel.onClickListener)
+ updateChipPadding(
+ chipModel,
+ chipBackgroundView,
+ chipTextView,
+ chipTimeView,
+ )
+
+ // Accessibility
+ setChipAccessibility(chipModel, chipView, chipBackgroundView)
+
+ // Colors
+ val textColor = chipModel.colors.text(chipContext)
+ chipTimeView.setTextColor(textColor)
+ chipTextView.setTextColor(textColor)
+ (chipBackgroundView.background as GradientDrawable).color =
+ chipModel.colors.background(chipContext)
+ }
+ is OngoingActivityChipModel.Hidden -> {
+ // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
+ // [Chronometer.start].
+ chipTimeView.stop()
+ }
+ }
+ }
+
+ private fun setChipIcon(
+ chipModel: OngoingActivityChipModel.Shown,
+ backgroundView: ChipBackgroundContainer,
+ defaultIconView: ImageView,
+ ) {
+ // Always remove any previously set custom icon. If we have a new custom icon, we'll re-add
+ // it.
+ backgroundView.removeView(backgroundView.getCustomIconView())
+
+ val iconTint = chipModel.colors.text(defaultIconView.context)
+
+ when (val icon = chipModel.icon) {
+ null -> {
+ defaultIconView.visibility = View.GONE
+ }
+ is OngoingActivityChipModel.ChipIcon.SingleColorIcon -> {
+ IconViewBinder.bind(icon.impl, defaultIconView)
+ defaultIconView.visibility = View.VISIBLE
+ defaultIconView.tintView(iconTint)
+ }
+ is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
+ StatusBarRonChips.assertInNewMode()
+ IconViewBinder.bind(icon.impl, defaultIconView)
+ defaultIconView.visibility = View.VISIBLE
+ defaultIconView.untintView()
+ }
+ is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
+ // Hide the default icon since we'll show this custom icon instead.
+ defaultIconView.visibility = View.GONE
+
+ // Add the new custom icon:
+ // 1. Set up the right visual params.
+ val iconView = icon.impl
+ with(iconView) {
+ id = CUSTOM_ICON_VIEW_ID
+ // TODO(b/354930838): Update the content description to not include "phone" and
+ // maybe include the app name.
+ contentDescription =
+ context.resources.getString(R.string.ongoing_phone_call_content_description)
+ tintView(iconTint)
+ }
+
+ // 2. If we just reinflated the view, we may need to detach the icon view from the
+ // old chip before we reattach it to the new one.
+ // See also: NotificationIconContainerViewBinder#bindIcons.
+ val currentParent = iconView.parent as? ViewGroup
+ if (currentParent != null && currentParent != backgroundView) {
+ currentParent.removeView(iconView)
+ currentParent.removeTransientView(iconView)
+ }
+
+ // 3: Add the icon as the starting view.
+ backgroundView.addView(
+ iconView,
+ /* index= */ 0,
+ generateCustomIconLayoutParams(iconView),
+ )
+ }
+ }
+ }
+
+ private fun View.getCustomIconView(): StatusBarIconView? {
+ return this.findViewById(CUSTOM_ICON_VIEW_ID)
+ }
+
+ private fun ImageView.tintView(color: Int) {
+ this.imageTintList = ColorStateList.valueOf(color)
+ }
+
+ private fun ImageView.untintView() {
+ this.imageTintList = null
+ }
+
+ private fun generateCustomIconLayoutParams(iconView: ImageView): FrameLayout.LayoutParams {
+ val customIconSize =
+ iconView.context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_activity_chip_embedded_padding_icon_size
+ )
+ return FrameLayout.LayoutParams(customIconSize, customIconSize)
+ }
+
+ private fun setChipMainContent(
+ chipModel: OngoingActivityChipModel.Shown,
+ chipTextView: TextView,
+ chipTimeView: ChipChronometer,
+ ) {
+ when (chipModel) {
+ is OngoingActivityChipModel.Shown.Countdown -> {
+ chipTextView.text = chipModel.secondsUntilStarted.toString()
+ chipTextView.visibility = View.VISIBLE
+
+ chipTimeView.hide()
+ }
+ is OngoingActivityChipModel.Shown.Text -> {
+ chipTextView.text = chipModel.text
+ chipTextView.visibility = View.VISIBLE
+
+ chipTimeView.hide()
+ }
+ is OngoingActivityChipModel.Shown.Timer -> {
+ ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
+ chipTimeView.visibility = View.VISIBLE
+
+ chipTextView.visibility = View.GONE
+ }
+ is OngoingActivityChipModel.Shown.IconOnly -> {
+ chipTextView.visibility = View.GONE
+ chipTimeView.hide()
+ }
+ }
+ }
+
+ private fun ChipChronometer.hide() {
+ // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
+ // [Chronometer.start].
+ this.stop()
+ this.visibility = View.GONE
+ }
+
+ private fun updateChipPadding(
+ chipModel: OngoingActivityChipModel.Shown,
+ backgroundView: View,
+ chipTextView: TextView,
+ chipTimeView: ChipChronometer,
+ ) {
+ if (chipModel.icon != null) {
+ if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
+ // If the icon is a custom [StatusBarIconView], then it should've come from
+ // `Notification.smallIcon`, which is required to embed its own paddings. We need to
+ // adjust the other paddings to make everything look good :)
+ backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon()
+ chipTextView.setTextPaddingForEmbeddedPaddingIcon()
+ chipTimeView.setTextPaddingForEmbeddedPaddingIcon()
+ } else {
+ backgroundView.setBackgroundPaddingForNormalIcon()
+ chipTextView.setTextPaddingForNormalIcon()
+ chipTimeView.setTextPaddingForNormalIcon()
+ }
+ } else {
+ backgroundView.setBackgroundPaddingForNoIcon()
+ chipTextView.setTextPaddingForNoIcon()
+ chipTimeView.setTextPaddingForNoIcon()
+ }
+ }
+
+ private fun View.setTextPaddingForEmbeddedPaddingIcon() {
+ val newPaddingEnd =
+ context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_activity_chip_text_end_padding_for_embedded_padding_icon
+ )
+ setPaddingRelative(
+ // The icon should embed enough padding between the icon and time view.
+ /* start= */ 0,
+ this.paddingTop,
+ newPaddingEnd,
+ this.paddingBottom,
+ )
+ }
+
+ private fun View.setTextPaddingForNormalIcon() {
+ this.setPaddingRelative(
+ this.context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_activity_chip_icon_text_padding
+ ),
+ paddingTop,
+ // The background view will contain the right end padding.
+ /* end= */ 0,
+ paddingBottom,
+ )
+ }
+
+ private fun View.setTextPaddingForNoIcon() {
+ // The background view will have even start & end paddings, so we don't want the text view
+ // to add any additional padding.
+ this.setPaddingRelative(/* start= */ 0, paddingTop, /* end= */ 0, paddingBottom)
+ }
+
+ private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() {
+ val sidePadding =
+ context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
+ )
+ setPaddingRelative(
+ sidePadding,
+ paddingTop,
+ sidePadding,
+ paddingBottom,
+ )
+ }
+
+ private fun View.setBackgroundPaddingForNormalIcon() {
+ val sidePadding =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding)
+ setPaddingRelative(
+ sidePadding,
+ paddingTop,
+ sidePadding,
+ paddingBottom,
+ )
+ }
+
+ private fun View.setBackgroundPaddingForNoIcon() {
+ // The padding for the normal icon is also appropriate for no icon.
+ setBackgroundPaddingForNormalIcon()
+ }
+
+ private fun setChipAccessibility(
+ chipModel: OngoingActivityChipModel.Shown,
+ chipView: View,
+ chipBackgroundView: View,
+ ) {
+ when (chipModel) {
+ is OngoingActivityChipModel.Shown.Countdown -> {
+ // Set as assertive so talkback will announce the countdown
+ chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+ }
+ is OngoingActivityChipModel.Shown.Timer,
+ is OngoingActivityChipModel.Shown.Text,
+ is OngoingActivityChipModel.Shown.IconOnly -> {
+ chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
+ }
+ }
+ // Clickable chips need to be a minimum size for accessibility purposes, but let
+ // non-clickable chips be smaller.
+ if (chipModel.onClickListener != null) {
+ chipBackgroundView.minimumWidth =
+ chipBackgroundView.context.resources.getDimensionPixelSize(
+ R.dimen.min_clickable_item_size
+ )
+ } else {
+ chipBackgroundView.minimumWidth = 0
+ }
+ }
+
+ @IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
index 8a5165d8..4b0fc5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
@@ -39,6 +39,24 @@
Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
}
+ /**
+ * The chip should have the given background color, and text color that matches dark/light
+ * theme.
+ */
+ data class Custom(val backgroundColorInt: Int) : ColorsModel {
+ override fun background(context: Context): ColorStateList =
+ ColorStateList.valueOf(backgroundColorInt)
+
+ // TODO(b/361346412): When dark theme changes, the chip should automatically re-render with
+ // the right text color. Right now, it has the right text color when the chip is first
+ // created but the color doesn't update if dark theme changes.
+ override fun text(context: Context) =
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.materialColorOnSurface,
+ )
+ }
+
/** The chip should have a red background with white text. */
data object Red : ColorsModel {
override fun background(context: Context): ColorStateList {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/MultipleOngoingActivityChipsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/MultipleOngoingActivityChipsModel.kt
new file mode 100644
index 0000000..d2555b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/MultipleOngoingActivityChipsModel.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.statusbar.chips.ui.model
+
+/** Models multiple active ongoing activity chips at once. */
+data class MultipleOngoingActivityChipsModel(
+ /** The primary chip to show. This will *always* be shown. */
+ val primary: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+ /**
+ * The secondary chip to show. If there's not enough room in the status bar, this chip will
+ * *not* be shown.
+ */
+ val secondary: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+) {
+ init {
+ if (
+ primary is OngoingActivityChipModel.Hidden &&
+ secondary is OngoingActivityChipModel.Shown
+ ) {
+ throw IllegalArgumentException("`secondary` cannot be Shown if `primary` is Hidden")
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 04c4516..24c1b87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
@@ -27,6 +28,7 @@
import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
+import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
@@ -87,111 +89,252 @@
) : InternalChipModel
}
- private val internalChip: Flow<InternalChipModel> =
- combine(
- screenRecordChipViewModel.chip,
- shareToAppChipViewModel.chip,
- castToOtherDeviceChipViewModel.chip,
- callChipViewModel.chip,
- demoRonChipViewModel.chip,
- ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon ->
- logger.log(
- TAG,
- LogLevel.INFO,
- {
- str1 = screenRecord.logName
- str2 = shareToApp.logName
- str3 = castToOtherDevice.logName
- },
- { "Chips: ScreenRecord=$str1 > ShareToApp=$str2 > CastToOther=$str3..." },
- )
- logger.log(
- TAG,
- LogLevel.INFO,
- {
- str1 = call.logName
- str2 = demoRon.logName
- },
- { "... > Call=$str1 > DemoRon=$str2" }
- )
- // This `when` statement shows the priority order of the chips.
- when {
- // Screen recording also activates the media projection APIs, so whenever the
- // screen recording chip is active, the media projection chip would also be
- // active. We want the screen-recording-specific chip shown in this case, so we
- // give the screen recording chip priority. See b/296461748.
- screenRecord is OngoingActivityChipModel.Shown ->
- InternalChipModel.Shown(ChipType.ScreenRecord, screenRecord)
- shareToApp is OngoingActivityChipModel.Shown ->
- InternalChipModel.Shown(ChipType.ShareToApp, shareToApp)
- castToOtherDevice is OngoingActivityChipModel.Shown ->
- InternalChipModel.Shown(ChipType.CastToOtherDevice, castToOtherDevice)
- call is OngoingActivityChipModel.Shown ->
- InternalChipModel.Shown(ChipType.Call, call)
- demoRon is OngoingActivityChipModel.Shown -> {
- StatusBarRonChips.assertInNewMode()
- InternalChipModel.Shown(ChipType.DemoRon, demoRon)
- }
- else -> {
- // We should only get here if all chip types are hidden
- check(screenRecord is OngoingActivityChipModel.Hidden)
- check(shareToApp is OngoingActivityChipModel.Hidden)
- check(castToOtherDevice is OngoingActivityChipModel.Hidden)
- check(call is OngoingActivityChipModel.Hidden)
- check(demoRon is OngoingActivityChipModel.Hidden)
- InternalChipModel.Hidden(
- screenRecord = screenRecord,
- shareToApp = shareToApp,
- castToOtherDevice = castToOtherDevice,
- call = call,
- demoRon = demoRon,
- )
- }
- }
- }
+ private data class ChipBundle(
+ val screenRecord: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+ val shareToApp: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+ val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+ val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+ val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+ )
- /**
- * A flow modeling the chip that should be shown in the status bar after accounting for possibly
- * multiple ongoing activities and animation requirements.
- *
- * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment] is responsible for
- * actually displaying the chip.
- */
- val chip: StateFlow<OngoingActivityChipModel> =
- internalChip
- .pairwise(initialValue = DEFAULT_INTERNAL_HIDDEN_MODEL)
- .map { (old, new) ->
- if (old is InternalChipModel.Shown && new is InternalChipModel.Hidden) {
- // If we're transitioning from showing the chip to hiding the chip, different
- // chips require different animation behaviors. For example, the screen share
- // chips shouldn't animate if the user stopped the screen share from the dialog
- // (see b/353249803#comment4), but the call chip should always animate.
- //
- // This `when` block makes sure that when we're transitioning from Shown to
- // Hidden, we check what chip type was previously showing and we use that chip
- // type's hide animation behavior.
- when (old.type) {
- ChipType.ScreenRecord -> new.screenRecord
- ChipType.ShareToApp -> new.shareToApp
- ChipType.CastToOtherDevice -> new.castToOtherDevice
- ChipType.Call -> new.call
- ChipType.DemoRon -> new.demoRon
- }
- } else if (new is InternalChipModel.Shown) {
- // If we have a chip to show, always show it.
- new.model
- } else {
- // In the Hidden -> Hidden transition, it shouldn't matter which hidden model we
- // choose because no animation should happen regardless.
- OngoingActivityChipModel.Hidden()
- }
+ /** Bundles all the incoming chips into one object to easily pass to various flows. */
+ private val incomingChipBundle =
+ combine(
+ screenRecordChipViewModel.chip,
+ shareToAppChipViewModel.chip,
+ castToOtherDeviceChipViewModel.chip,
+ callChipViewModel.chip,
+ demoRonChipViewModel.chip,
+ ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon ->
+ logger.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = screenRecord.logName
+ str2 = shareToApp.logName
+ str3 = castToOtherDevice.logName
+ },
+ { "Chips: ScreenRecord=$str1 > ShareToApp=$str2 > CastToOther=$str3..." },
+ )
+ logger.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = call.logName
+ str2 = demoRon.logName
+ },
+ { "... > Call=$str1 > DemoRon=$str2" }
+ )
+ ChipBundle(
+ screenRecord = screenRecord,
+ shareToApp = shareToApp,
+ castToOtherDevice = castToOtherDevice,
+ call = call,
+ demoRon = demoRon,
+ )
}
// Some of the chips could have timers in them and we don't want the start time
// for those timers to get reset for any reason. So, as soon as any subscriber has
// requested the chip information, we maintain it forever by using
// [SharingStarted.Lazily]. See b/347726238.
+ .stateIn(scope, SharingStarted.Lazily, ChipBundle())
+
+ private val internalChip: Flow<InternalChipModel> =
+ incomingChipBundle.map { bundle -> pickMostImportantChip(bundle).mostImportantChip }
+
+ /**
+ * A flow modeling the primary chip that should be shown in the status bar after accounting for
+ * possibly multiple ongoing activities and animation requirements.
+ *
+ * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment] is responsible for
+ * actually displaying the chip.
+ */
+ val primaryChip: StateFlow<OngoingActivityChipModel> =
+ internalChip
+ .pairwise(initialValue = DEFAULT_INTERNAL_HIDDEN_MODEL)
+ .map { (old, new) -> createOutputModel(old, new) }
.stateIn(scope, SharingStarted.Lazily, OngoingActivityChipModel.Hidden())
+ /**
+ * Equivalent to [MultipleOngoingActivityChipsModel] but using the internal models to do some
+ * state tracking before we get the final output.
+ */
+ private data class InternalMultipleOngoingActivityChipsModel(
+ val primary: InternalChipModel,
+ val secondary: InternalChipModel,
+ )
+
+ private val internalChips: Flow<InternalMultipleOngoingActivityChipsModel> =
+ incomingChipBundle.map { bundle ->
+ // First: Find the most important chip.
+ val primaryChipResult = pickMostImportantChip(bundle)
+ val primaryChip = primaryChipResult.mostImportantChip
+ if (primaryChip is InternalChipModel.Hidden) {
+ // If the primary chip is hidden, the secondary chip will also be hidden, so just
+ // pass the same Hidden model for both.
+ InternalMultipleOngoingActivityChipsModel(primaryChip, primaryChip)
+ } else {
+ // Then: Find the next most important chip.
+ val secondaryChip =
+ pickMostImportantChip(primaryChipResult.remainingChips).mostImportantChip
+ InternalMultipleOngoingActivityChipsModel(primaryChip, secondaryChip)
+ }
+ }
+
+ /**
+ * A flow modeling the primary chip that should be shown in the status bar after accounting for
+ * possibly multiple ongoing activities and animation requirements.
+ *
+ * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment] is responsible for
+ * actually displaying the chip.
+ */
+ val chips: StateFlow<MultipleOngoingActivityChipsModel> =
+ if (!Flags.statusBarRonChips()) {
+ // Multiple chips are only allowed with RONs. If the flag isn't on, use just the
+ // primary chip.
+ primaryChip
+ .map {
+ MultipleOngoingActivityChipsModel(
+ primary = it,
+ secondary = OngoingActivityChipModel.Hidden(),
+ )
+ }
+ .stateIn(
+ scope,
+ SharingStarted.Lazily,
+ MultipleOngoingActivityChipsModel(),
+ )
+ } else {
+ internalChips
+ .pairwise(initialValue = DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL)
+ .map { (old, new) ->
+ val correctPrimary = createOutputModel(old.primary, new.primary)
+ val correctSecondary = createOutputModel(old.secondary, new.secondary)
+ MultipleOngoingActivityChipsModel(correctPrimary, correctSecondary)
+ }
+ .stateIn(
+ scope,
+ SharingStarted.Lazily,
+ MultipleOngoingActivityChipsModel(),
+ )
+ }
+
+ /** A data class representing the return result of [pickMostImportantChip]. */
+ private data class MostImportantChipResult(
+ val mostImportantChip: InternalChipModel,
+ val remainingChips: ChipBundle,
+ )
+
+ /**
+ * Finds the most important chip from the given [bundle].
+ *
+ * This function returns that most important chip, and it also returns any remaining chips that
+ * still want to be shown after filtering out the most important chip.
+ */
+ private fun pickMostImportantChip(bundle: ChipBundle): MostImportantChipResult {
+ // This `when` statement shows the priority order of the chips.
+ return when {
+ bundle.screenRecord is OngoingActivityChipModel.Shown ->
+ MostImportantChipResult(
+ mostImportantChip =
+ InternalChipModel.Shown(ChipType.ScreenRecord, bundle.screenRecord),
+ remainingChips =
+ bundle.copy(
+ screenRecord = OngoingActivityChipModel.Hidden(),
+ // Screen recording also activates the media projection APIs, which
+ // means that whenever the screen recording chip is active, the
+ // share-to-app chip would also be active. (Screen recording is a
+ // special case of share-to-app, where the app receiving the share is
+ // specifically System UI.)
+ // We want only the screen-recording-specific chip to be shown in this
+ // case. If we did have screen recording as the primary chip, we need to
+ // suppress the share-to-app chip to make sure they don't both show.
+ // See b/296461748.
+ shareToApp = OngoingActivityChipModel.Hidden(),
+ )
+ )
+ bundle.shareToApp is OngoingActivityChipModel.Shown ->
+ MostImportantChipResult(
+ mostImportantChip =
+ InternalChipModel.Shown(ChipType.ShareToApp, bundle.shareToApp),
+ remainingChips = bundle.copy(shareToApp = OngoingActivityChipModel.Hidden()),
+ )
+ bundle.castToOtherDevice is OngoingActivityChipModel.Shown ->
+ MostImportantChipResult(
+ mostImportantChip =
+ InternalChipModel.Shown(
+ ChipType.CastToOtherDevice,
+ bundle.castToOtherDevice,
+ ),
+ remainingChips =
+ bundle.copy(castToOtherDevice = OngoingActivityChipModel.Hidden()),
+ )
+ bundle.call is OngoingActivityChipModel.Shown ->
+ MostImportantChipResult(
+ mostImportantChip = InternalChipModel.Shown(ChipType.Call, bundle.call),
+ remainingChips = bundle.copy(call = OngoingActivityChipModel.Hidden()),
+ )
+ bundle.demoRon is OngoingActivityChipModel.Shown -> {
+ StatusBarRonChips.assertInNewMode()
+ MostImportantChipResult(
+ mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon),
+ remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()),
+ )
+ }
+ else -> {
+ // We should only get here if all chip types are hidden
+ check(bundle.screenRecord is OngoingActivityChipModel.Hidden)
+ check(bundle.shareToApp is OngoingActivityChipModel.Hidden)
+ check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden)
+ check(bundle.call is OngoingActivityChipModel.Hidden)
+ check(bundle.demoRon is OngoingActivityChipModel.Hidden)
+ MostImportantChipResult(
+ mostImportantChip =
+ InternalChipModel.Hidden(
+ screenRecord = bundle.screenRecord,
+ shareToApp = bundle.shareToApp,
+ castToOtherDevice = bundle.castToOtherDevice,
+ call = bundle.call,
+ demoRon = bundle.demoRon,
+ ),
+ // All the chips are already hidden, so no need to filter anything out of the
+ // bundle.
+ remainingChips = bundle,
+ )
+ }
+ }
+ }
+
+ private fun createOutputModel(
+ old: InternalChipModel,
+ new: InternalChipModel,
+ ): OngoingActivityChipModel {
+ return if (old is InternalChipModel.Shown && new is InternalChipModel.Hidden) {
+ // If we're transitioning from showing the chip to hiding the chip, different
+ // chips require different animation behaviors. For example, the screen share
+ // chips shouldn't animate if the user stopped the screen share from the dialog
+ // (see b/353249803#comment4), but the call chip should always animate.
+ //
+ // This `when` block makes sure that when we're transitioning from Shown to
+ // Hidden, we check what chip type was previously showing and we use that chip
+ // type's hide animation behavior.
+ return when (old.type) {
+ ChipType.ScreenRecord -> new.screenRecord
+ ChipType.ShareToApp -> new.shareToApp
+ ChipType.CastToOtherDevice -> new.castToOtherDevice
+ ChipType.Call -> new.call
+ ChipType.DemoRon -> new.demoRon
+ }
+ } else if (new is InternalChipModel.Shown) {
+ // If we have a chip to show, always show it.
+ new.model
+ } else {
+ // In the Hidden -> Hidden transition, it shouldn't matter which hidden model we
+ // choose because no animation should happen regardless.
+ OngoingActivityChipModel.Hidden()
+ }
+ }
+
companion object {
private const val TAG = "ChipsViewModel"
@@ -203,5 +346,11 @@
call = OngoingActivityChipModel.Hidden(),
demoRon = OngoingActivityChipModel.Hidden(),
)
+
+ private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL =
+ InternalMultipleOngoingActivityChipsModel(
+ primary = DEFAULT_INTERNAL_HIDDEN_MODEL,
+ secondary = DEFAULT_INTERNAL_HIDDEN_MODEL,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
index 01083d9..412c8c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.commandline
+import androidx.core.graphics.toColorInt
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@@ -164,10 +165,23 @@
?: Result.failure(ArgParseError("Failed to parse $value as a float"))
}
+// See https://developer.android.com/reference/android/graphics/Color#parseColor(java.lang.String)
+// for the supported formats of the color string. tl;dr: #RRGGBB, #AARRGGBB, or a basic color name
+// like "red" or "green". For the RRGGBB values, the `#` needs to be escaped. Use `"\\#RRGGBB"` in
+// the command to escape the `#` correctly.
+private val parseColor: ValueParser<Int> = ValueParser { value ->
+ try {
+ Result.success(value.toColorInt())
+ } catch (e: IllegalArgumentException) {
+ Result.failure(ArgParseError("Failed to parse $value as a color: $e"))
+ }
+}
+
/** Default parsers that can be use as-is, or [map]ped to another type */
object Type {
val Boolean = parseBoolean
val Int = parseInt
val Float = parseFloat
val String = parseString
+ val Color = parseColor
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index 400f8af..dac0102 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -21,6 +21,7 @@
import com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.AirplaneModeTile
import com.android.systemui.qs.tiles.BluetoothTile
@@ -95,21 +96,21 @@
@IntoMap
@StringKey(AIRPLANE_MODE_TILE_SPEC)
fun provideAirplaneModeAvailabilityInteractor(
- impl: AirplaneModeTileDataInteractor
+ impl: AirplaneModeTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(DATA_SAVER_TILE_SPEC)
fun provideDataSaverAvailabilityInteractor(
- impl: DataSaverTileDataInteractor
+ impl: DataSaverTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(INTERNET_TILE_SPEC)
fun provideInternetAvailabilityInteractor(
- impl: InternetTileDataInteractor
+ impl: InternetTileDataInteractor
): QSTileAvailabilityInteractor
companion object {
@@ -149,6 +150,7 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
policy = QSTilePolicy.Restricted(listOf(UserManager.DISALLOW_AIRPLANE_MODE)),
+ category = TileCategory.CONNECTIVITY,
)
/** Inject AirplaneModeTile into tileViewModelMap in QSModule */
@@ -180,6 +182,7 @@
labelRes = R.string.data_saver,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
/** Inject DataSaverTile into tileViewModelMap in QSModule */
@@ -211,6 +214,7 @@
labelRes = R.string.quick_settings_internet_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
/** Inject InternetTile into tileViewModelMap in QSModule */
@@ -242,6 +246,7 @@
labelRes = R.string.quick_settings_hotspot_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
@Provides
@@ -256,6 +261,7 @@
labelRes = R.string.quick_settings_cast_title,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
@Provides
@@ -270,6 +276,7 @@
labelRes = R.string.quick_settings_bluetooth_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 3a2f95e..6d0148a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -22,7 +22,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
-import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
@@ -32,6 +32,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -43,6 +44,7 @@
import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.BooleanFlowOperators;
import com.android.systemui.util.kotlin.JavaAdapter;
import java.io.PrintWriter;
@@ -70,7 +72,8 @@
private final VisibilityLocationProvider mVisibilityLocationProvider;
private final VisualStabilityProvider mVisualStabilityProvider;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final CommunalInteractor mCommunalInteractor;
+ private final CommunalSceneInteractor mCommunalSceneInteractor;
+ private final ShadeInteractor mShadeInteractor;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final VisualStabilityCoordinatorLogger mLogger;
@@ -110,7 +113,8 @@
VisibilityLocationProvider visibilityLocationProvider,
VisualStabilityProvider visualStabilityProvider,
WakefulnessLifecycle wakefulnessLifecycle,
- CommunalInteractor communalInteractor,
+ CommunalSceneInteractor communalSceneInteractor,
+ ShadeInteractor shadeInteractor,
KeyguardTransitionInteractor keyguardTransitionInteractor,
VisualStabilityCoordinatorLogger logger) {
mHeadsUpManager = headsUpManager;
@@ -122,7 +126,8 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mDelayableExecutor = delayableExecutor;
- mCommunalInteractor = communalInteractor;
+ mCommunalSceneInteractor = communalSceneInteractor;
+ mShadeInteractor = shadeInteractor;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mLogger = logger;
@@ -141,7 +146,11 @@
this::onShadeOrQsClosingChanged);
mJavaAdapter.alwaysCollectFlow(mShadeAnimationInteractor.isLaunchingActivity(),
this::onLaunchingActivityChanged);
- mJavaAdapter.alwaysCollectFlow(mCommunalInteractor.isIdleOnCommunal(),
+ mJavaAdapter.alwaysCollectFlow(
+ BooleanFlowOperators.INSTANCE.allOf(
+ mCommunalSceneInteractor.isIdleOnCommunal(),
+ BooleanFlowOperators.INSTANCE.not(mShadeInteractor.isAnyFullyExpanded())
+ ),
this::onCommunalShowingChanged);
if (SceneContainerFlag.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
index af21e75..d36412c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
@@ -16,17 +16,23 @@
package com.android.systemui.statusbar.notification.data
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.SecureSettingsRepositoryModule
import com.android.systemui.settings.SystemSettingsRepositoryModule
import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionLogger
import dagger.Module
import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
@Module(includes = [SecureSettingsRepositoryModule::class, SystemSettingsRepositoryModule::class])
object NotificationSettingsRepositoryModule {
@@ -42,6 +48,19 @@
backgroundScope,
backgroundDispatcher,
secureSettingsRepository,
- systemSettingsRepository
- )
+ systemSettingsRepository)
+
+ @Provides
+ @IntoMap
+ @ClassKey(NotificationSettingsRepository::class)
+ @SysUISingleton
+ fun provideCoreStartable(
+ @Application applicationScope: CoroutineScope,
+ repository: NotificationSettingsRepository,
+ logger: VisualInterruptionDecisionLogger
+ ) = CoreStartable {
+ applicationScope.launch {
+ repository.isCooldownEnabled.collect { value -> logger.logCooldownSetting(value) }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 0efd5f1..ec0827b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.util.NotificationChannels
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SystemSettings
import com.android.systemui.util.time.SystemClock
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
@@ -279,7 +280,8 @@
private val uiEventLogger: UiEventLogger,
private val context: Context,
private val notificationManager: NotificationManager,
- private val logger: VisualInterruptionDecisionLogger
+ private val logger: VisualInterruptionDecisionLogger,
+ private val systemSettings: SystemSettings,
) :
VisualInterruptionFilter(
types = setOf(PEEK, PULSE),
@@ -300,6 +302,11 @@
// education HUNs.
private var hasShownOnceForDebug = false
+ // Sometimes the kotlin flow value is false even when the cooldown setting is true (b/356768397)
+ // so let's directly check settings until we confirm that the flow is initialized and in sync
+ // with the real settings value.
+ private var isCooldownFlowInSync = false
+
private fun shouldShowEdu(): Boolean {
val forceShowOnce = SystemProperties.get(FORCE_SHOW_AVALANCHE_EDU_ONCE, "").equals("1")
return !hasSeenEdu || (forceShowOnce && !hasShownOnceForDebug)
@@ -479,6 +486,15 @@
}
private fun isCooldownEnabled(): Boolean {
- return settingsInteractor.isCooldownEnabled.value
+ val isEnabledFromFlow = settingsInteractor.isCooldownEnabled.value
+ if (isCooldownFlowInSync) {
+ return isEnabledFromFlow
+ }
+ val isEnabled =
+ systemSettings.getInt(Settings.System.NOTIFICATION_COOLDOWN_ENABLED, /* def */ 1) == 1
+ if (isEnabled == isEnabledFromFlow) {
+ isCooldownFlowInSync = true
+ }
+ return isEnabled
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
index b83259d..38cab82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
@@ -102,6 +102,15 @@
{ "AvalancheSuppressor: $str1" }
)
}
+
+ fun logCooldownSetting(isEnabled: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ { bool1 = isEnabled },
+ { "Cooldown enabled: $bool1" }
+ )
+ }
}
private const val TAG = "VisualInterruptionDecisionProvider"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 2f8711a..d4466f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -195,7 +195,8 @@
uiEventLogger,
context,
notificationManager,
- logger
+ logger,
+ systemSettings
)
)
avalancheProvider.register()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
index ce4356a..18d4a04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
@@ -18,7 +18,7 @@
/**
* NotificationList proto from atoms.proto, duplicated here so that it's accessible in the build.
- * Must be kept in sync with the version in atoms.proto.
+ * Must be kept in sync with the version in stats/atoms/sysui/sysui_atoms.proto.
*/
message Notification {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
index 2c462b7..77c4130 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
@@ -198,12 +198,22 @@
parentView,
/* attachToRoot= */ false
) as EnRouteView
-
InflatedContentViewHolder(newView) {
EnRouteViewBinder.bindWhileAttached(newView, createViewModel())
}
}
- RichOngoingNotificationViewType.Expanded,
+ RichOngoingNotificationViewType.Expanded -> {
+ val newView =
+ LayoutInflater.from(systemUiContext)
+ .inflate(
+ R.layout.notification_template_en_route_expanded,
+ parentView,
+ /* attachToRoot= */ false
+ ) as EnRouteView
+ InflatedContentViewHolder(newView) {
+ EnRouteViewBinder.bindWhileAttached(newView, createViewModel())
+ }
+ }
RichOngoingNotificationViewType.HeadsUp -> NullContentView
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 4be638f..1431b28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -173,7 +173,8 @@
}
/**
- * @return Height of the notifications panel without top padding when expansion completes.
+ * @return Height of the available space for the notification content, when the shade
+ * expansion completes.
*/
public float getStackEndHeight() {
return mStackEndHeight;
@@ -276,17 +277,18 @@
}
/**
- * @see #getStackHeight()
+ * @return Height of the notification content returned by {@link #getStackEndHeight()}, but
+ * interpolated by the shade expansion fraction.
*/
- public void setStackHeight(float stackHeight) {
- mStackHeight = stackHeight;
+ public float getInterpolatedStackHeight() {
+ return mStackHeight;
}
/**
- * @return Height of notifications panel interpolated by the expansion fraction.
+ * @see #getInterpolatedStackHeight()
*/
- public float getStackHeight() {
- return mStackHeight;
+ public void setInterpolatedStackHeight(float stackHeight) {
+ mStackHeight = stackHeight;
}
@Inject
@@ -531,8 +533,15 @@
if (mDozeAmount == 1.0f && !isPulseExpanding()) {
return mShelf.getHeight();
}
- int height = (int) Math.max(mLayoutMinHeight,
- Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
+ int height;
+ if (SceneContainerFlag.isEnabled()) {
+ // TODO(b/192348384): This is probably incorrect as mContentHeight is not up to date.
+ // Consider removing usages of getInnerHeight in flexiglass if possible.
+ height = (int) Math.min(mLayoutHeight, mContentHeight) - mTopPadding;
+ } else {
+ height = (int) Math.max(mLayoutMinHeight,
+ Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
+ }
if (ignorePulseHeight) {
return height;
}
@@ -569,6 +578,7 @@
}
public void setLayoutMinHeight(int layoutMinHeight) {
+ SceneContainerFlag.assertInLegacyMode();
mLayoutMinHeight = layoutMinHeight;
}
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 1f767aa..1214440a 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
@@ -789,7 +789,6 @@
private void onJustBeforeDraw() {
if (SceneContainerFlag.isEnabled()) {
if (mChildrenUpdateRequested) {
- updateForcedScroll();
updateChildren();
mChildrenUpdateRequested = false;
}
@@ -874,7 +873,7 @@
y = (int) (mAmbientState.getStackY());
drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y);
- y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
+ y = (int) (mAmbientState.getStackY() + mAmbientState.getInterpolatedStackHeight());
drawDebugInfo(canvas, y, Color.LTGRAY,
/* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y);
@@ -1123,11 +1122,13 @@
@Override
public void addStackHeightChangedListener(@NonNull Runnable runnable) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mStackHeightChangedListeners.addIfAbsent(runnable);
}
@Override
public void removeStackHeightChangedListener(@NonNull Runnable runnable) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mStackHeightChangedListeners.remove(runnable);
}
@@ -1233,7 +1234,6 @@
if (mAmbientState.getStackTop() != stackTop) {
mAmbientState.setStackTop(stackTop);
onTopPaddingChanged(/* animate = */ isAddOrRemoveAnimationPending());
- setExpandedHeight(mExpandedHeight);
}
}
@@ -1308,8 +1308,10 @@
}
private void updateAlgorithmLayoutMinHeight() {
- mAmbientState.setLayoutMinHeight(mQsFullScreen || isHeadsUpTransition()
- ? getLayoutMinHeightInternal() : 0);
+ if (!SceneContainerFlag.isEnabled()) {
+ mAmbientState.setLayoutMinHeight(mQsFullScreen || isHeadsUpTransition()
+ ? getLayoutMinHeightInternal() : 0);
+ }
}
/**
@@ -1387,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,
@@ -1397,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) {
@@ -1476,7 +1478,7 @@
@VisibleForTesting
public void updateStackEndHeightAndStackHeight(float fraction) {
- final float oldStackHeight = mAmbientState.getStackHeight();
+ final float oldStackHeight = mAmbientState.getInterpolatedStackHeight();
if (SceneContainerFlag.isEnabled()) {
final float endHeight;
if (!shouldSkipHeightUpdate()) {
@@ -1484,20 +1486,20 @@
} else {
endHeight = mAmbientState.getStackEndHeight();
}
- updateStackHeight(endHeight, fraction);
+ updateInterpolatedStackHeight(endHeight, fraction);
} else {
if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
final float endHeight = updateStackEndHeight(
getHeight(), getEmptyBottomMarginInternal(), getTopPadding());
- updateStackHeight(endHeight, fraction);
+ updateInterpolatedStackHeight(endHeight, fraction);
} else {
// Always updateStackHeight to prevent jumps in the stack height when this fraction
// suddenly reapplies after a freeze.
final float endHeight = mAmbientState.getStackEndHeight();
- updateStackHeight(endHeight, fraction);
+ updateInterpolatedStackHeight(endHeight, fraction);
}
}
- if (oldStackHeight != mAmbientState.getStackHeight()) {
+ if (oldStackHeight != mAmbientState.getInterpolatedStackHeight()) {
requestChildrenUpdate();
}
}
@@ -1531,7 +1533,7 @@
}
@VisibleForTesting
- public void updateStackHeight(float endHeight, float fraction) {
+ public void updateInterpolatedStackHeight(float endHeight, float fraction) {
if (!newAodTransition()) {
// During the (AOD<=>LS) transition where dozeAmount is changing,
// apply dozeAmount to stack height instead of expansionFraction
@@ -1541,7 +1543,7 @@
fraction = 1f - dozeAmount;
}
}
- mAmbientState.setStackHeight(
+ mAmbientState.setInterpolatedStackHeight(
MathUtils.lerp(endHeight * StackScrollAlgorithm.START_FRACTION,
endHeight, fraction));
}
@@ -1570,8 +1572,11 @@
// Update the expand progress between started/stopped events
mAmbientState.setExpansionFraction(expandFraction);
- // TODO(b/332577544): don't convert to height which then converts to the fraction again
- setExpandedHeight(expandFraction * getHeight());
+
+ if (!shouldSkipHeightUpdate()) {
+ updateStackEndHeightAndStackHeight(expandFraction);
+ updateExpandedHeight(expandFraction);
+ }
// expansion stopped event requires that the expandFraction has already been updated
if (!nowExpanding && wasExpanding) {
@@ -1580,6 +1585,19 @@
}
}
+ private void updateExpandedHeight(float expandFraction) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ float expandedHeight = expandFraction * getHeight();
+ setIsExpanded(expandedHeight > 0);
+
+ if (mExpandedHeight != expandedHeight) {
+ mExpandedHeight = expandedHeight;
+ updateAlgorithmHeightAndPadding();
+ requestChildrenUpdate();
+ notifyAppearChangedListeners();
+ }
+ }
+
@Override
public void setQsExpandFraction(float expandFraction) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
@@ -1592,6 +1610,7 @@
* @param height the expanded height of the panel
*/
public void setExpandedHeight(float height) {
+ SceneContainerFlag.assertInLegacyMode();
final boolean skipHeightUpdate = shouldSkipHeightUpdate();
updateStackPosition();
@@ -1723,6 +1742,7 @@
* Measured relative to the resting position.
*/
private float getExpandTranslationStart() {
+ SceneContainerFlag.assertInLegacyMode();
return -getTopPadding() + getMinExpansionHeight() - mShelf.getIntrinsicHeight();
}
@@ -1731,6 +1751,7 @@
* Measured in absolute height.
*/
private float getAppearStartPosition() {
+ SceneContainerFlag.assertInLegacyMode();
if (isHeadsUpTransition()) {
final NotificationSection firstVisibleSection = getFirstVisibleSection();
final int pinnedHeight = firstVisibleSection != null
@@ -1786,6 +1807,7 @@
* have the shelf on its own)
*/
private float getAppearEndPosition() {
+ SceneContainerFlag.assertInLegacyMode();
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
return getAppearEndPositionLegacy();
}
@@ -1846,7 +1868,7 @@
*/
@FloatRange(from = -1.0, to = 1.0)
public float calculateAppearFraction(float height) {
- if (isHeadsUpTransition()) {
+ if (isHeadsUpTransition() && !SceneContainerFlag.isEnabled()) {
// HUN is a special case because fraction can go negative if swiping up. And for now
// it must go negative as other pieces responsible for proper translation up assume
// negative value for HUN going up.
@@ -1977,7 +1999,8 @@
}
public void lockScrollTo(View v) {
- if (mForcedScroll == v) {
+ // NSSL shouldn't handle scrolling with SceneContainer enabled.
+ if (mForcedScroll == v || SceneContainerFlag.isEnabled()) {
return;
}
mForcedScroll = v;
@@ -1985,6 +2008,10 @@
}
public boolean scrollTo(View v) {
+ // NSSL shouldn't handle scrolling with SceneContainer enabled.
+ if (SceneContainerFlag.isEnabled()) {
+ return false;
+ }
ExpandableView expandableView = (ExpandableView) v;
int positionInLinearLayout = getPositionInLinearLayout(v);
int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
@@ -2006,6 +2033,7 @@
* the IME.
*/
private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
+ SceneContainerFlag.assertInLegacyMode();
return positionInLinearLayout + v.getIntrinsicHeight() +
getImeInset() - getHeight()
+ ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
@@ -2418,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;
@@ -2522,10 +2555,33 @@
}
@VisibleForTesting
- void updateContentHeight() {
+ void updateStackHeight() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+
+ final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0;
+ final int footerIntrinsicHeight =
+ mFooterView != null ? mFooterView.getIntrinsicHeight() : 0;
+ final int notificationsHeight = (int) mNotificationStackSizeCalculator.computeHeight(
+ /* notificationStackScrollLayout= */ this,
+ mMaxDisplayedNotifications,
+ shelfIntrinsicHeight
+ );
+ mIntrinsicContentHeight = notificationsHeight;
+ final int fullStackHeight = notificationsHeight + footerIntrinsicHeight + mBottomPadding;
+ if (mScrollViewFields.getIntrinsicStackHeight() != fullStackHeight) {
+ mScrollViewFields.setIntrinsicStackHeight(fullStackHeight);
+ notifyStackHeightChangedListeners();
+ }
+ }
+
+ private void updateContentHeight() {
+ if (SceneContainerFlag.isEnabled()) {
+ updateStackHeight();
+ return;
+ }
+
final float scrimTopPadding = getScrimTopPaddingOrZero();
final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0;
- final int footerIntrinsicHeight = mFooterView != null ? mFooterView.getIntrinsicHeight() : 0;
final float height =
(int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight(
/* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
@@ -2536,19 +2592,15 @@
// state the maxPanelHeight and the contentHeight should be bigger
mContentHeight =
(int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding);
- mScrollViewFields.setIntrinsicStackHeight(
- (int) (getIntrinsicPadding() + mIntrinsicContentHeight + footerIntrinsicHeight
- + mBottomPadding));
updateScrollability();
clampScrollPosition();
updateStackPosition();
mAmbientState.setContentHeight(mContentHeight);
-
- notifyStackHeightChangedListeners();
}
@Override
public int getIntrinsicStackHeight() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0;
return mScrollViewFields.getIntrinsicStackHeight();
}
@@ -2584,6 +2636,9 @@
}
private void updateScrollability() {
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
boolean scrollable = !mQsFullScreen && getScrollRange() > 0;
if (scrollable != mScrollable) {
mScrollable = scrollable;
@@ -2593,6 +2648,7 @@
}
private void updateForwardAndBackwardScrollability() {
+ SceneContainerFlag.assertInLegacyMode();
boolean forwardScrollable = mScrollable && !mScrollAdapter.isScrolledToBottom();
boolean backwardsScrollable = mScrollable && !mScrollAdapter.isScrolledToTop();
boolean changed = forwardScrollable != mForwardScrollable
@@ -2750,6 +2806,7 @@
}
private int getLayoutMinHeightInternal() {
+ SceneContainerFlag.assertInLegacyMode();
if (isHeadsUpTransition()) {
ExpandableNotificationRow trackedHeadsUpRow = mAmbientState.getTrackedHeadsUpRow();
if (trackedHeadsUpRow.isAboveShelf()) {
@@ -2829,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)) {
@@ -3008,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;
@@ -3029,6 +3089,7 @@
* @return the amount of scrolling needed to start clipping notifications.
*/
private int getScrollAmountToScrollBoundary() {
+ SceneContainerFlag.assertInLegacyMode();
if (mShouldUseSplitNotificationShade) {
return mSidePaddings;
}
@@ -3630,6 +3691,10 @@
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL: {
+ // If scene container is active, NSSL should not control its own scrolling.
+ if (SceneContainerFlag.isEnabled()) {
+ return false;
+ }
if (!mIsBeingDragged) {
final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
if (vscroll != 0) {
@@ -3658,7 +3723,7 @@
if (!isScrollingEnabled()) {
return false;
}
- if (isInsideQsHeader(ev) && !mIsBeingDragged) {
+ if (!isInScrollableRegion(ev) && !mIsBeingDragged) {
return false;
}
mForcedScroll = null;
@@ -3826,11 +3891,26 @@
return mFlingAfterUpEvent;
}
- protected boolean isInsideQsHeader(MotionEvent ev) {
- if (SceneContainerFlag.isEnabled()) {
- return ev.getY() < mAmbientState.getStackTop();
+ /** Is this touch event inside the scrollable region? */
+ @VisibleForTesting
+ boolean isInScrollableRegion(MotionEvent ev) {
+ if (!SceneContainerFlag.isEnabled()) {
+ return !isInsideQsHeader(ev);
+ }
+ ShadeScrimShape shape = mScrollViewFields.getScrimClippingShape();
+ if (shape == null) {
+ return true; // When there is no scrim, consider this event scrollable.
}
+ ShadeScrimBounds bounds = shape.getBounds();
+ return ev.getX() >= bounds.getLeft()
+ && ev.getX() <= bounds.getRight()
+ && ev.getY() >= bounds.getTop()
+ && ev.getY() <= bounds.getBottom();
+ }
+
+ protected boolean isInsideQsHeader(MotionEvent ev) {
+ SceneContainerFlag.assertInLegacyMode();
if (QSComposeFragment.isEnabled()) {
if (mQSHeaderBoundsProvider == null) {
return false;
@@ -4132,6 +4212,11 @@
*/
@Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled.
+ if (SceneContainerFlag.isEnabled()) {
+ return super.performAccessibilityActionInternal(action, arguments);
+ }
+
if (super.performAccessibilityActionInternal(action, arguments)) {
return true;
}
@@ -4893,6 +4978,11 @@
@Override
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
super.onInitializeAccessibilityEventInternal(event);
+ // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled.
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
+
event.setScrollable(mScrollable);
event.setMaxScrollX(mScrollX);
event.setScrollY(mOwnScrollY);
@@ -4902,6 +4992,11 @@
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
+ // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled.
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
+
if (mScrollable) {
info.setScrollable(true);
if (mBackwardScrollable) {
@@ -5087,10 +5182,12 @@
}
boolean isQsFullScreen() {
+ SceneContainerFlag.assertInLegacyMode();
return mQsFullScreen;
}
public void setQsExpansionFraction(float qsExpansionFraction) {
+ SceneContainerFlag.assertInLegacyMode();
boolean footerAffected = mQsExpansionFraction != qsExpansionFraction
&& (mQsExpansionFraction == 1 || qsExpansionFraction == 1);
mQsExpansionFraction = qsExpansionFraction;
@@ -5134,6 +5231,7 @@
}
private void updateOnScrollChange() {
+ SceneContainerFlag.assertInLegacyMode();
if (mScrollListener != null) {
mScrollListener.accept(mOwnScrollY);
}
@@ -5306,7 +5404,9 @@
println(pw, "intrinsicContentHeight", mIntrinsicContentHeight);
println(pw, "contentHeight", mContentHeight);
println(pw, "intrinsicPadding", mIntrinsicPadding);
- println(pw, "topPadding", getTopPadding());
+ if (!SceneContainerFlag.isEnabled()) {
+ println(pw, "topPadding", getTopPadding());
+ }
println(pw, "bottomPadding", mBottomPadding);
dumpRoundedRectClipping(pw);
println(pw, "requestedClipBounds", mRequestedClipBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 08d3e9f..bcdc3bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1275,6 +1275,7 @@
}
public void setQsExpansionFraction(float expansionFraction) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setQsExpansionFraction(expansionFraction);
}
@@ -1408,6 +1409,7 @@
}
public float calculateAppearFraction(float height) {
+ SceneContainerFlag.assertInLegacyMode();
return mView.calculateAppearFraction(height);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index 580431a..969ff1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -68,6 +68,7 @@
if (mLabelTextId != null) {
mLabelView.setText(mLabelTextId);
}
+ mLabelView.setAccessibilityHeading(true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 0c2b5ae..cccac4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -575,7 +575,8 @@
final float shelfHeight = showingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f;
final float scrimPadding = getScrimTopPaddingOrZero(ambientState);
- final float stackHeight = ambientState.getStackHeight() - shelfHeight - scrimPadding;
+ final float stackHeight =
+ ambientState.getInterpolatedStackHeight() - shelfHeight - scrimPadding;
final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
if (stackEndHeight == 0f) {
// This should not happen, since even when the shade is empty we show EmptyShadeView
@@ -681,7 +682,10 @@
// doesn't get updated quickly enough and can cause the footer to flash when
// closing the shade. As such, we temporarily also check the ambientState directly.
if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
- viewState.hidden = true;
+ // Note: This is no longer necessary in flexiglass.
+ if (!SceneContainerFlag.isEnabled()) {
+ viewState.hidden = true;
+ }
} else {
final float footerEnd = algorithmState.mCurrentExpandedYPosition
+ view.getIntrinsicHeight();
@@ -690,7 +694,6 @@
noSpaceForFooter || (ambientState.isClearAllInProgress()
&& !hasNonClearableNotifs(algorithmState));
}
-
} else {
final boolean shadeClosed = !ambientState.isShadeExpanded();
final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
@@ -734,7 +737,7 @@
|| ambientState.getDozeAmount() == 1f
|| bypassPulseNotExpanding
? ambientState.getInnerHeight()
- : ambientState.getStackHeight();
+ : ambientState.getInterpolatedStackHeight();
final float shelfStart = stackBottom
- ambientState.getShelf().getIntrinsicHeight()
- mPaddingBetweenElements;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index d770b20..dc9615c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -188,15 +188,26 @@
.startHistoryIntent(view, /* showHistory= */ true)
},
)
- launch {
- viewModel.shouldIncludeFooterView.collect { animatedVisibility ->
- footerView.setVisible(
- /* visible = */ animatedVisibility.value,
- /* animate = */ animatedVisibility.isAnimating,
- )
+ if (SceneContainerFlag.isEnabled) {
+ launch {
+ viewModel.shouldShowFooterView.collect { animatedVisibility ->
+ footerView.setVisible(
+ /* visible = */ animatedVisibility.value,
+ /* animate = */ animatedVisibility.isAnimating,
+ )
+ }
}
+ } else {
+ launch {
+ viewModel.shouldIncludeFooterView.collect { animatedVisibility ->
+ footerView.setVisible(
+ /* visible = */ animatedVisibility.value,
+ /* animate = */ animatedVisibility.isAnimating,
+ )
+ }
+ }
+ launch { viewModel.shouldHideFooterView.collect { footerView.setShouldBeHidden(it) } }
}
- launch { viewModel.shouldHideFooterView.collect { footerView.setShouldBeHidden(it) } }
disposableHandle.awaitCancellationThenDispose()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index aa1911e..5ae5a32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -62,7 +62,6 @@
viewModel: SharedNotificationContainerViewModel,
): DisposableHandle {
val disposables = DisposableHandles()
-
disposables +=
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
@@ -87,10 +86,7 @@
}
val burnInParams = MutableStateFlow(BurnInParameters())
- val viewState =
- ViewStateAccessor(
- alpha = { controller.getAlpha() },
- )
+ val viewState = ViewStateAccessor(alpha = { controller.getAlpha() })
/*
* For animation sensitive coroutines, immediately run just like applicationScope does
@@ -108,7 +104,7 @@
addUpdateListener { animation ->
controller.setMaxAlphaForKeyguard(
animation.animatedFraction,
- "SharedNotificationContainerVB (collapseFadeIn)"
+ "SharedNotificationContainerVB (collapseFadeIn)",
)
}
start()
@@ -153,7 +149,7 @@
launch { viewModel.translationX.collect { x -> controller.translationX = x } }
launch {
- viewModel.keyguardAlpha(viewState).collect {
+ viewModel.keyguardAlpha(viewState, this).collect {
controller.setMaxAlphaForKeyguard(it, "SharedNotificationContainerVB")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index e55492e6..4e2a46d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.kotlin.combine
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
@@ -120,6 +121,7 @@
* This essentially corresponds to having the view set to INVISIBLE.
*/
val shouldHideFooterView: Flow<Boolean> by lazy {
+ SceneContainerFlag.assertInLegacyMode()
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
@@ -143,6 +145,7 @@
* be hidden by another condition (see [shouldHideFooterView] above).
*/
val shouldIncludeFooterView: Flow<AnimatedValue<Boolean>> by lazy {
+ SceneContainerFlag.assertInLegacyMode()
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(AnimatedValue.NotAnimating(false))
} else {
@@ -207,6 +210,76 @@
}
}
+ // This flow replaces shouldHideFooterView+shouldIncludeFooterView in flexiglass.
+ val shouldShowFooterView: Flow<AnimatedValue<Boolean>> by lazy {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
+ flowOf(AnimatedValue.NotAnimating(false))
+ } else {
+ combine(
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ userSetupInteractor.isUserSetUp,
+ notificationStackInteractor.isShowingOnLockscreen,
+ shadeInteractor.isQsFullscreen,
+ remoteInputInteractor.isRemoteInputActive,
+ shadeInteractor.shadeExpansion.map { it < 0.5f }.distinctUntilChanged(),
+ ) {
+ hasNotifications,
+ isUserSetUp,
+ isShowingOnLockscreen,
+ qsFullScreen,
+ isRemoteInputActive,
+ shadeLessThanHalfwayExpanded ->
+ when {
+ !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Hide the footer until the user setup is complete, to prevent access
+ // to settings (b/193149550).
+ !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Do not show the footer if the lockscreen is visible (incl. AOD),
+ // except if the shade is opened on top. See also b/219680200.
+ // Do not animate, as that makes the footer appear briefly when
+ // transitioning between the shade and keyguard.
+ isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
+ // Do not show the footer if quick settings are fully expanded (except
+ // for the foldable split shade view). See b/201427195 && b/222699879.
+ qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Hide the footer if remote input is active (i.e. user is replying to a
+ // notification). See b/75984847.
+ isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // If the shade is not expanded enough, the footer shouldn't be visible.
+ shadeLessThanHalfwayExpanded -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ else -> VisibilityChange.APPEAR_WITH_ANIMATION
+ }
+ }
+ .distinctUntilChanged(
+ // Equivalent unless visibility changes
+ areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
+ a.visible == b.visible
+ }
+ )
+ // Should we animate the visibility change?
+ .sample(
+ // TODO(b/322167853): This check is currently duplicated in FooterViewModel,
+ // but instead it should be a field in ShadeAnimationInteractor.
+ combine(
+ shadeInteractor.isShadeFullyExpanded,
+ shadeInteractor.isShadeTouchable,
+ ::Pair
+ )
+ .onStart { emit(Pair(false, false)) }
+ ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
+ // Animate if the shade is interactive, but NOT on the lockscreen. Having
+ // animations enabled while on the lockscreen makes the footer appear briefly
+ // when transitioning between the shade and keyguard.
+ val shouldAnimate =
+ isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
+ AnimatableEvent(visibilityChange.visible, shouldAnimate)
+ }
+ .toAnimatedValueFlow()
+ .dumpWhileCollecting("shouldShowFooterView")
+ .flowOn(bgDispatcher)
+ }
+ }
+
enum class VisibilityChange(val visible: Boolean, val canAnimate: Boolean) {
DISAPPEAR_WITHOUT_ANIMATION(visible = false, canAnimate = false),
DISAPPEAR_WITH_ANIMATION(visible = false, canAnimate = true),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index aed00d8..e34eb61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -20,7 +20,6 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import androidx.annotation.VisibleForTesting
-import com.android.compose.animation.scene.SceneKey
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -29,7 +28,6 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
@@ -41,7 +39,6 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
-import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
@@ -73,7 +70,6 @@
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
-import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
@@ -166,10 +162,9 @@
* before the other.
*/
private val isShadeLocked: Flow<Boolean> =
- combine(
- keyguardInteractor.statusBarState.map { it == SHADE_LOCKED },
- isAnyExpanded,
- ) { isShadeLocked, isAnyExpanded ->
+ combine(keyguardInteractor.statusBarState.map { it == SHADE_LOCKED }, isAnyExpanded) {
+ isShadeLocked,
+ isAnyExpanded ->
isShadeLocked && isAnyExpanded
}
.stateIn(
@@ -220,23 +215,20 @@
keyguardTransitionInteractor.isFinishedIn(ALTERNATE_BOUNCER),
keyguardTransitionInteractor.isFinishedIn(
scene = Scenes.Bouncer,
- stateWithoutSceneContainer = PRIMARY_BOUNCER
+ stateWithoutSceneContainer = PRIMARY_BOUNCER,
),
keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
)
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
- initialValue = false
+ initialValue = false,
)
.dumpValue("isOnLockscreen")
/** Are we purely on the keyguard without the shade/qs? */
val isOnLockscreenWithoutShade: Flow<Boolean> =
- combine(
- isOnLockscreen,
- isAnyExpanded,
- ) { isKeyguard, isAnyExpanded ->
+ combine(isOnLockscreen, isAnyExpanded) { isKeyguard, isAnyExpanded ->
isKeyguard && !isAnyExpanded
}
.stateIn(
@@ -251,16 +243,16 @@
combine(
keyguardTransitionInteractor.isFinishedIn(
scene = Scenes.Communal,
- stateWithoutSceneContainer = GLANCEABLE_HUB
+ stateWithoutSceneContainer = GLANCEABLE_HUB,
),
anyOf(
keyguardTransitionInteractor.isInTransition(
edge = Edge.create(to = Scenes.Communal),
- edgeWithoutSceneContainer = Edge.create(to = GLANCEABLE_HUB)
+ edgeWithoutSceneContainer = Edge.create(to = GLANCEABLE_HUB),
),
keyguardTransitionInteractor.isInTransition(
edge = Edge.create(from = Scenes.Communal),
- edgeWithoutSceneContainer = Edge.create(from = GLANCEABLE_HUB)
+ edgeWithoutSceneContainer = Edge.create(from = GLANCEABLE_HUB),
),
),
) { isOnGlanceableHub, transitioningToOrFromHub ->
@@ -271,10 +263,7 @@
/** Are we purely on the glanceable hub without the shade/qs? */
val isOnGlanceableHubWithoutShade: Flow<Boolean> =
- combine(
- isOnGlanceableHub,
- isAnyExpanded,
- ) { isGlanceableHub, isAnyExpanded ->
+ combine(isOnGlanceableHub, isAnyExpanded) { isGlanceableHub, isAnyExpanded ->
isGlanceableHub && !isAnyExpanded
}
.stateIn(
@@ -286,10 +275,9 @@
/** Are we on the dream without the shade/qs? */
private val isDreamingWithoutShade: Flow<Boolean> =
- combine(
- keyguardTransitionInteractor.isFinishedIn(DREAMING),
- isAnyExpanded,
- ) { isDreaming, isAnyExpanded ->
+ combine(keyguardTransitionInteractor.isFinishedIn(DREAMING), isAnyExpanded) {
+ isDreaming,
+ isAnyExpanded ->
isDreaming && !isAnyExpanded
}
.stateIn(
@@ -310,7 +298,7 @@
keyguardTransitionInteractor.isInTransition(
edge = Edge.create(from = LOCKSCREEN, to = AOD)
),
- ::Pair
+ ::Pair,
)
.transformWhile { (isOnLockscreenWithoutShade, aodTransitionIsRunning) ->
// Wait until the AOD transition is complete before terminating
@@ -375,7 +363,7 @@
keyguardTransitionInteractor.isInTransition,
shadeInteractor.qsExpansion,
)
- .onStart { emit(Triple(0f, false, 0f)) }
+ .onStart { emit(Triple(0f, false, 0f)) },
) { onLockscreen, bounds, paddingTop, (top, isInTransitionToAnyState, qsExpansion) ->
if (onLockscreen) {
bounds.copy(top = bounds.top - paddingTop)
@@ -383,10 +371,7 @@
// When QS expansion > 0, it should directly set the top padding so do not
// animate it
val animate = qsExpansion == 0f && !isInTransitionToAnyState
- bounds.copy(
- top = top,
- isAnimated = animate,
- )
+ bounds.copy(top = top, isAnimated = animate)
}
}
.stateIn(
@@ -404,10 +389,9 @@
private val alphaForShadeAndQsExpansion: Flow<Float> =
interactor.configurationBasedDimensions
.flatMapLatest { configurationBasedDimensions ->
- combineTransform(
- shadeInteractor.shadeExpansion,
- shadeInteractor.qsExpansion,
- ) { shadeExpansion, qsExpansion ->
+ combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
+ shadeExpansion,
+ qsExpansion ->
if (shadeExpansion > 0f || qsExpansion > 0f) {
if (configurationBasedDimensions.useSplitShade) {
emit(1f)
@@ -424,47 +408,6 @@
.onStart { emit(1f) }
.dumpWhileCollecting("alphaForShadeAndQsExpansion")
- private val isTransitioningToHiddenKeyguard: Flow<Boolean> =
- flow {
- while (currentCoroutineContext().isActive) {
- emit(false)
- // Ensure states are inactive to start
- allOf(isNotOnState(OCCLUDED), isNotOnState(GONE, Scenes.Gone)).first { it }
- // Wait for a qualifying transition to begin
- anyOf(
- transitionToIsRunning(Edge.create(to = OCCLUDED)),
- transitionToIsRunning(
- edge = Edge.create(to = Scenes.Gone),
- edgeWithoutSceneContainer = Edge.create(to = GONE)
- )
- )
- .first { it }
- emit(true)
- // Now await the signal that SHADE state has been reached or the transition was
- // reversed. Until SHADE state has been replaced it is the only source of when
- // it is considered safe to reset alpha to 1f for HUNs.
- combine(
- keyguardInteractor.statusBarState,
- allOf(isNotOnState(OCCLUDED), isNotOnState(GONE, Scenes.Gone))
- ) { statusBarState, stateIsReversed ->
- statusBarState == SHADE || stateIsReversed
- }
- .first { it }
- }
- }
- .dumpWhileCollecting("isTransitioningToHiddenKeyguard")
-
- private fun isNotOnState(stateWithoutSceneContainer: KeyguardState, scene: SceneKey? = null) =
- keyguardTransitionInteractor
- .transitionValue(scene = scene, stateWithoutSceneContainer = stateWithoutSceneContainer)
- .map { it == 0f }
-
- private fun transitionToIsRunning(edge: Edge, edgeWithoutSceneContainer: Edge? = null) =
- keyguardTransitionInteractor
- .transition(edge = edge, edgeWithoutSceneContainer = edgeWithoutSceneContainer)
- .map { it.value > 0f && it.transitionState == RUNNING }
- .onStart { emit(false) }
-
val panelAlpha = keyguardInteractor.panelAlpha
private fun bouncerToGoneNotificationAlpha(viewState: ViewStateAccessor): Flow<Float> =
@@ -478,49 +421,72 @@
}
.dumpWhileCollecting("bouncerToGoneNotificationAlpha")
- fun keyguardAlpha(viewState: ViewStateAccessor): Flow<Float> {
- // All transition view models are mututally exclusive, and safe to merge
- val alphaTransitions =
- merge(
- keyguardInteractor.dismissAlpha.dumpWhileCollecting(
- "keyguardInteractor.dismissAlpha"
- ),
- bouncerToGoneNotificationAlpha(viewState),
- aodToGoneTransitionViewModel.notificationAlpha(viewState),
- aodToLockscreenTransitionViewModel.notificationAlpha,
- aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
- dozingToLockscreenTransitionViewModel.lockscreenAlpha,
- dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState),
- dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
- goneToAodTransitionViewModel.notificationAlpha,
- goneToDreamingTransitionViewModel.lockscreenAlpha,
- goneToDozingTransitionViewModel.notificationAlpha,
- goneToLockscreenTransitionViewModel.lockscreenAlpha,
- lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
- lockscreenToGoneTransitionViewModel.notificationAlpha(viewState),
- lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
- lockscreenToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
- occludedToAodTransitionViewModel.lockscreenAlpha,
- occludedToGoneTransitionViewModel.notificationAlpha(viewState),
- occludedToLockscreenTransitionViewModel.lockscreenAlpha,
- primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
- glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
- lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
- )
-
+ private fun alphaForTransitions(viewState: ViewStateAccessor): Flow<Float> {
return merge(
- alphaTransitions,
- // These remaining cases handle alpha changes within an existing state, such as
- // shade expansion or swipe to dismiss
- combineTransform(
- isTransitioningToHiddenKeyguard,
- alphaForShadeAndQsExpansion,
- ) { isTransitioningToHiddenKeyguard, alphaForShadeAndQsExpansion ->
- if (!isTransitioningToHiddenKeyguard) {
- emit(alphaForShadeAndQsExpansion)
- }
- },
- )
+ keyguardInteractor.dismissAlpha.dumpWhileCollecting("keyguardInteractor.dismissAlpha"),
+ // All transition view models are mututally exclusive, and safe to merge
+ bouncerToGoneNotificationAlpha(viewState),
+ aodToGoneTransitionViewModel.notificationAlpha(viewState),
+ aodToLockscreenTransitionViewModel.notificationAlpha,
+ aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ dozingToLockscreenTransitionViewModel.lockscreenAlpha,
+ dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
+ goneToAodTransitionViewModel.notificationAlpha,
+ goneToDreamingTransitionViewModel.lockscreenAlpha,
+ goneToDozingTransitionViewModel.notificationAlpha,
+ goneToLockscreenTransitionViewModel.lockscreenAlpha,
+ lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
+ lockscreenToGoneTransitionViewModel.notificationAlpha(viewState),
+ lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
+ lockscreenToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
+ occludedToAodTransitionViewModel.lockscreenAlpha,
+ occludedToGoneTransitionViewModel.notificationAlpha(viewState),
+ occludedToLockscreenTransitionViewModel.lockscreenAlpha,
+ primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
+ glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
+ lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
+ )
+ }
+
+ fun keyguardAlpha(viewState: ViewStateAccessor, scope: CoroutineScope): Flow<Float> {
+ // Transitions are not (yet) authoritative for NSSL; they still rely on StatusBarState to
+ // help determine when the device has fully moved to GONE or OCCLUDED state. Once SHADE
+ // state has been set, let shade alpha take over
+ val isKeyguardNotVisible =
+ combine(
+ anyOf(
+ keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f },
+ keyguardTransitionInteractor
+ .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
+ .map { it == 1f },
+ ),
+ keyguardInteractor.statusBarState,
+ ) { isKeyguardNotVisibleInState, statusBarState ->
+ isKeyguardNotVisibleInState && statusBarState == SHADE
+ }
+
+ // This needs to continue collecting the current value so that when it is selected in the
+ // flatMapLatest below, the last value gets emitted, to avoid the randomness of `merge`.
+ val alphaForTransitionsAndShade =
+ merge(alphaForTransitions(viewState), alphaForShadeAndQsExpansion)
+ .stateIn(
+ // Use view-level scope instead of ApplicationScope, to prevent collection that
+ // never stops
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = 1f,
+ )
+ .dumpValue("alphaForTransitionsAndShade")
+
+ return isKeyguardNotVisible
+ .flatMapLatest { isKeyguardNotVisible ->
+ if (isKeyguardNotVisible) {
+ alphaForShadeAndQsExpansion
+ } else {
+ alphaForTransitionsAndShade
+ }
+ }
.distinctUntilChanged()
.dumpWhileCollecting("keyguardAlpha")
}
@@ -543,9 +509,8 @@
)
// Manually emit on start because [notificationAlpha] only starts emitting
// when transitions start.
- .onStart { emit(1f) }
- ) { isOnGlanceableHubWithoutShade, isOnLockscreen, isDreamingWithoutShade, alpha,
- ->
+ .onStart { emit(1f) },
+ ) { isOnGlanceableHubWithoutShade, isOnLockscreen, isDreamingWithoutShade, alpha ->
if ((isOnGlanceableHubWithoutShade || isDreamingWithoutShade) && !isOnLockscreen) {
// Notifications should not be visible on the glanceable hub.
// TODO(b/321075734): implement a way to actually set the notifications to
@@ -580,7 +545,7 @@
merge(
keyguardInteractor.keyguardTranslationY,
occludedToLockscreenTransitionViewModel.lockscreenTranslationY,
- )
+ ),
) { burnInY, isOnLockscreenWithoutShade, translationY ->
if (isOnLockscreenWithoutShade) {
burnInY + translationY
@@ -604,7 +569,7 @@
unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false)
} else {
emptyFlow()
- }
+ },
)
.dumpWhileCollecting("translationX")
@@ -634,7 +599,7 @@
primaryBouncerToGoneTransitionViewModel.showAllNotifications,
alternateBouncerToGoneTransitionViewModel.showAllNotifications,
)
- .onStart { emit(false) }
+ .onStart { emit(false) },
) { isOnLockscreen, statusBarState, showAllNotifications ->
statusBarState == SHADE_LOCKED || !isOnLockscreen || showAllNotifications
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 2e1ab38..bb5aa23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -20,16 +20,17 @@
import android.graphics.Rect
import android.os.LocaleList
import android.view.View.LAYOUT_DIRECTION_RTL
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
-@SysUISingleton
-class ConfigurationControllerImpl @Inject constructor(
- @Application context: Context,
- ) : ConfigurationController {
+class ConfigurationControllerImpl
+@AssistedInject
+constructor(
+ @Assisted private val context: Context,
+) : ConfigurationController {
private val listeners: MutableList<ConfigurationListener> = ArrayList()
private val lastConfig = Configuration()
@@ -40,18 +41,17 @@
private val inCarMode: Boolean
private var uiMode: Int = 0
private var localeList: LocaleList? = null
- private val context: Context
private var layoutDirection: Int
private var orientation = Configuration.ORIENTATION_UNDEFINED
init {
val currentConfig = context.resources.configuration
- this.context = context
fontScale = currentConfig.fontScale
density = currentConfig.densityDpi
smallestScreenWidth = currentConfig.smallestScreenWidthDp
maxBounds.set(currentConfig.windowConfiguration.maxBounds)
- inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
+ inCarMode =
+ currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
Configuration.UI_MODE_TYPE_CAR
uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
localeList = currentConfig.locales
@@ -60,29 +60,20 @@
override fun notifyThemeChanged() {
// Avoid concurrent modification exception
- val listeners = synchronized(this.listeners) {
- ArrayList(this.listeners)
- }
+ val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onThemeChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onThemeChanged() }
}
override fun onConfigurationChanged(newConfig: Configuration) {
// Avoid concurrent modification exception
- val listeners = synchronized(this.listeners) {
- ArrayList(this.listeners)
- }
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onConfigChanged(newConfig)
- }
+ val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onConfigChanged(newConfig) }
val fontScale = newConfig.fontScale
val density = newConfig.densityDpi
val uiMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
val uiModeChanged = uiMode != this.uiMode
- if (density != this.density || fontScale != this.fontScale ||
- inCarMode && uiModeChanged) {
+ if (density != this.density || fontScale != this.fontScale || inCarMode && uiModeChanged) {
listeners.filterForEach({ this.listeners.contains(it) }) {
it.onDensityOrFontScaleChanged()
}
@@ -105,17 +96,13 @@
// would be a direct reference to windowConfiguration.maxBounds, so the if statement
// above would always fail. See b/245799099 for more information.
this.maxBounds.set(maxBounds)
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onMaxBoundsChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onMaxBoundsChanged() }
}
val localeList = newConfig.locales
if (localeList != this.localeList) {
this.localeList = localeList
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onLocaleListChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onLocaleListChanged() }
}
if (uiModeChanged) {
@@ -124,9 +111,7 @@
context.theme.applyStyle(context.themeResId, true)
this.uiMode = uiMode
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onUiModeChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onUiModeChanged() }
}
if (layoutDirection != newConfig.layoutDirection) {
@@ -137,9 +122,7 @@
}
if (lastConfig.updateFrom(newConfig) and ActivityInfo.CONFIG_ASSETS_PATHS != 0) {
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onThemeChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onThemeChanged() }
}
val newOrientation = newConfig.orientation
@@ -152,16 +135,12 @@
}
override fun addCallback(listener: ConfigurationListener) {
- synchronized(listeners) {
- listeners.add(listener)
- }
+ synchronized(listeners) { listeners.add(listener) }
listener.onDensityOrFontScaleChanged()
}
override fun removeCallback(listener: ConfigurationListener) {
- synchronized(listeners) {
- listeners.remove(listener)
- }
+ synchronized(listeners) { listeners.remove(listener) }
}
override fun isLayoutRtl(): Boolean {
@@ -176,6 +155,15 @@
else -> "err"
}
}
+
+ @AssistedFactory
+ interface Factory {
+ /**
+ * Creates a [ConfigurationController] that uses [context] to resolve the current
+ * configuration and resources.
+ */
+ fun create(context: Context): ConfigurationControllerImpl
+ }
}
// This could be done with a Collection.filter and Collection.forEach, but Collection.filter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
index 90ebaf2..8f4279e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import com.android.systemui.CoreStartable
+import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -26,7 +27,7 @@
class ConfigurationControllerStartable
@Inject
constructor(
- private val configurationController: ConfigurationController,
+ @GlobalConfig private val configurationController: ConfigurationController,
private val listeners: Set<@JvmSuppressWildcards ConfigurationListener>
) : CoreStartable {
override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 1efad3b..0e7beb9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -262,6 +262,9 @@
releaseAllImmediately();
mReleaseOnExpandFinish = false;
} else {
+ for (NotificationEntry entry: getAllEntries().toList()) {
+ entry.setSeenInShade(true);
+ }
for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
if (isHeadsUpEntry(entry.getKey())) {
// Maybe the heads-up was removed already
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index c3da7fc..178c318 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -435,13 +435,14 @@
/** Should only be called from {@link KeyguardStatusBarViewController}. */
void onOverlayChanged() {
- int theme = Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall);
- mCarrierLabel.setTextAppearance(theme);
+ final int carrierTheme = R.style.TextAppearance_StatusBar_Clock;
+ mCarrierLabel.setTextAppearance(carrierTheme);
mBatteryView.updatePercentView();
+ final int userSwitcherTheme = R.style.TextAppearance_StatusBar_UserChip;
TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name);
if (userSwitcherName != null) {
- userSwitcherName.setTextAppearance(theme);
+ userSwitcherName.setTextAppearance(userSwitcherTheme);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 56ea00c..7ef1e41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -22,6 +22,7 @@
import androidx.annotation.NonNull;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
@@ -43,17 +44,20 @@
private final UserManager mUserManager;
private final UserTracker mUserTracker;
private final LinkedList<UserInfo> mProfiles;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private boolean mListening;
private int mCurrentUser;
@Inject
public ManagedProfileControllerImpl(Context context, @Main Executor mainExecutor,
- UserTracker userTracker, UserManager userManager) {
+ UserTracker userTracker, UserManager userManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mMainExecutor = mainExecutor;
mUserManager = userManager;
mUserTracker = userTracker;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mProfiles = new LinkedList<>();
}
@@ -80,6 +84,7 @@
StatusBarManager statusBarManager = (StatusBarManager) mContext
.getSystemService(android.app.Service.STATUS_BAR_SERVICE);
statusBarManager.collapsePanels();
+ mKeyguardUpdateMonitor.awakenFromDream();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 05bd1a7..ba39c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -44,7 +44,7 @@
import androidx.lifecycle.Observer;
-import com.android.settingslib.notification.modes.ZenMode;
+import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -80,6 +80,7 @@
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor;
+import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.DateFormatUtil;
@@ -363,7 +364,7 @@
// Note that we're not fully replacing ZenModeController with ZenModeInteractor, so
// we listen for the extra event here but still add the ZMC callback.
mJavaAdapter.alwaysCollectFlow(mZenModeInteractor.getMainActiveMode(),
- this::onActiveModeChanged);
+ this::onMainActiveModeChanged);
}
mZenController.addCallback(mZenControllerCallback);
if (!Flags.statusBarScreenSharingChips()) {
@@ -395,20 +396,23 @@
() -> mResources.getString(R.string.accessibility_managed_profile));
}
- private void onActiveModeChanged(@Nullable ZenMode mode) {
+ private void onMainActiveModeChanged(@Nullable ZenModeInfo mainActiveMode) {
if (!usesModeIcons()) {
- Log.wtf(TAG, "onActiveModeChanged shouldn't be called if MODES_UI_ICONS is disabled");
+ Log.wtf(TAG, "onMainActiveModeChanged shouldn't run if MODES_UI_ICONS is disabled");
return;
}
- boolean visible = mode != null;
- if (visible) {
- // TODO: b/360399800 - Get the resource id, package, and cached drawable from the mode;
- // this is a shortcut for testing.
- String resPackage = mode.getIconKey().resPackage();
- int iconResId = mode.getIconKey().resId();
- mIconController.setResourceIcon(mSlotZen, resPackage, iconResId,
- /* preloadedIcon= */ null, mode.getName());
+ boolean visible = mainActiveMode != null;
+ if (visible) {
+ // Shape=FIXED_SPACE because mode icons can be from 3P packages and may not be square;
+ // we don't want to allow apps to set incredibly wide icons and take up too much space
+ // in the status bar.
+ mIconController.setResourceIcon(mSlotZen,
+ mainActiveMode.getIcon().key().resPackage(),
+ mainActiveMode.getIcon().key().resId(),
+ mainActiveMode.getIcon().drawable(),
+ mainActiveMode.getName(),
+ StatusBarIcon.Shape.FIXED_SPACE);
}
if (visible != mZenVisible) {
mIconController.setIconVisibility(mSlotZen, visible);
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/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 43f9af6..1ea26e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -61,6 +61,7 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags;
import com.android.systemui.bouncer.ui.BouncerView;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -70,8 +71,8 @@
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.DismissAction;
import com.android.systemui.keyguard.shared.model.Edge;
@@ -182,6 +183,7 @@
private boolean mBouncerShowingOverDream;
private int mAttemptsToShowBouncer = 0;
private DelayableExecutor mExecutor;
+ private boolean mIsSleeping = false;
private final PrimaryBouncerExpansionCallback mExpansionCallback =
new PrimaryBouncerExpansionCallback() {
@@ -713,7 +715,11 @@
* {@link #needsFullscreenBouncer()}.
*/
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset) {
- if (needsFullscreenBouncer() && !mDozing) {
+ boolean showBouncer = needsFullscreenBouncer() && !mDozing;
+ if (Flags.simPinRaceConditionOnRestart()) {
+ showBouncer = showBouncer && !mIsSleeping;
+ }
+ if (showBouncer) {
// The keyguard might be showing (already). So we need to hide it.
if (!primaryBouncerIsShowing()) {
if (SceneContainerFlag.isEnabled()) {
@@ -834,7 +840,7 @@
public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
boolean afterKeyguardGone, String message) {
- if (SceneContainerFlag.isEnabled()) {
+ if (ComposeBouncerFlags.INSTANCE.isEnabled()) {
if (r == null) {
return;
}
@@ -886,7 +892,7 @@
return;
}
- if (!SceneContainerFlag.isEnabled()) {
+ if (!ComposeBouncerFlags.INSTANCE.isEnabled()) {
mAfterKeyguardGoneAction = r;
mKeyguardGoneCancelAction = cancelAction;
mDismissActionWillAnimateOnKeyguard = r != null
@@ -960,7 +966,7 @@
* Adds a {@param runnable} to be executed after Keyguard is gone.
*/
public void addAfterKeyguardGoneRunnable(Runnable runnable) {
- if (SceneContainerFlag.isEnabled()) {
+ if (ComposeBouncerFlags.INSTANCE.isEnabled()) {
if (runnable != null) {
mKeyguardDismissActionInteractor.get().runAfterKeyguardGone(runnable);
}
@@ -992,7 +998,7 @@
} else {
showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset);
}
- if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing) {
+ if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing && isBouncerShowing()) {
hideAlternateBouncer(true);
mDismissCallbackRegistry.notifyDismissCancelled();
mPrimaryBouncerInteractor.setDismissAction(null, null);
@@ -1041,6 +1047,7 @@
@Override
public void onStartedWakingUp() {
+ mIsSleeping = false;
setRootViewAnimationDisabled(false);
NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
@@ -1054,6 +1061,7 @@
@Override
public void onStartedGoingToSleep() {
+ mIsSleeping = true;
setRootViewAnimationDisabled(true);
NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
@@ -1173,7 +1181,7 @@
// We update the state (which will show the keyguard) only if an animation will run on
// the keyguard. If there is no animation, we wait before updating the state so that we
// go directly from bouncer to launcher/app.
- if (SceneContainerFlag.isEnabled()) {
+ if (ComposeBouncerFlags.INSTANCE.isEnabled()) {
if (mKeyguardDismissActionInteractor.get().runDismissAnimationOnKeyguard()) {
updateStates();
}
@@ -1300,7 +1308,7 @@
}
private void executeAfterKeyguardGoneAction() {
- if (SceneContainerFlag.isEnabled()) {
+ if (ComposeBouncerFlags.INSTANCE.isEnabled()) {
return;
}
if (mAfterKeyguardGoneAction != null) {
@@ -1696,6 +1704,8 @@
pw.println(" Registered KeyguardViewManagerCallbacks:");
pw.println(" SceneContainerFlag enabled:"
+ SceneContainerFlag.isEnabled());
+ pw.println(" ComposeBouncerFlags enabled:"
+ + ComposeBouncerFlags.INSTANCE.isEnabled());
for (KeyguardViewManagerCallback callback : mCallbacks) {
pw.println(" " + callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index c046168..0ad1042a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -480,10 +480,16 @@
}
public static AlertDialog applyFlags(AlertDialog dialog) {
+ return applyFlags(dialog, true);
+ }
+
+ public static AlertDialog applyFlags(AlertDialog dialog, boolean showWhenLocked) {
final Window window = dialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
- window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ if (showWhenLocked) {
+ window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
window.getAttributes().setFitInsetsTypes(
window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
return dialog;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 5be4ba2..659cee3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -55,6 +55,7 @@
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.chips.shared.StatusBarRonChips;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
@@ -121,7 +122,8 @@
private MultiSourceMinAlphaController mEndSideAlphaController;
private LinearLayout mEndSideContent;
private View mClockView;
- private View mOngoingActivityChip;
+ private View mPrimaryOngoingActivityChip;
+ private View mSecondaryOngoingActivityChip;
private View mNotificationIconAreaInner;
// Visibilities come in from external system callers via disable flags, but we also sometimes
// modify the visibilities internally. We need to store both so that we don't accidentally
@@ -212,9 +214,16 @@
private boolean mHomeStatusBarAllowedByScene = true;
/**
- * True if there's an active ongoing activity that should be showing a chip and false otherwise.
+ * True if there's a primary active ongoing activity that should be showing a chip and false
+ * otherwise.
*/
- private boolean mHasOngoingActivity;
+ private boolean mHasPrimaryOngoingActivity;
+
+ /**
+ * True if there's a secondary active ongoing activity that should be showing a chip and false
+ * otherwise.
+ */
+ private boolean mHasSecondaryOngoingActivity;
/**
* Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives
@@ -354,7 +363,9 @@
mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content);
mEndSideAlphaController = new MultiSourceMinAlphaController(mEndSideContent);
mClockView = mStatusBar.findViewById(R.id.clock);
- mOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip);
+ mPrimaryOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip_primary);
+ mSecondaryOngoingActivityChip =
+ mStatusBar.findViewById(R.id.ongoing_activity_chip_secondary);
showEndSideContent(false);
showClock(false);
initOperatorName();
@@ -508,8 +519,11 @@
@Override
public void onOngoingActivityStatusChanged(
- boolean hasOngoingActivity, boolean shouldAnimate) {
- mHasOngoingActivity = hasOngoingActivity;
+ boolean hasPrimaryOngoingActivity,
+ boolean hasSecondaryOngoingActivity,
+ boolean shouldAnimate) {
+ mHasPrimaryOngoingActivity = hasPrimaryOngoingActivity;
+ mHasSecondaryOngoingActivity = hasSecondaryOngoingActivity;
updateStatusBarVisibilities(shouldAnimate);
}
@@ -554,7 +568,7 @@
boolean notifsChanged =
newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons();
boolean ongoingActivityChanged =
- newModel.getShowOngoingActivityChip() != previousModel.getShowOngoingActivityChip();
+ newModel.isOngoingActivityStatusDifferentFrom(previousModel);
if (notifsChanged || ongoingActivityChanged) {
updateNotificationIconAreaAndOngoingActivityChip(animate);
}
@@ -597,21 +611,26 @@
boolean showClock = externalModel.getShowClock() && !headsUpVisible;
- boolean showOngoingActivityChip;
+ boolean showPrimaryOngoingActivityChip;
if (Flags.statusBarScreenSharingChips()) {
// If this flag is on, the ongoing activity status comes from
- // CollapsedStatusBarViewBinder, which updates the mHasOngoingActivity variable.
- showOngoingActivityChip = mHasOngoingActivity;
+ // CollapsedStatusBarViewBinder, which updates the mHasPrimaryOngoingActivity variable.
+ showPrimaryOngoingActivityChip = mHasPrimaryOngoingActivity;
} else {
// If this flag is off, the only ongoing activity is the ongoing call, and we pull it
// from the controller directly.
- showOngoingActivityChip = mOngoingCallController.hasOngoingCall();
+ showPrimaryOngoingActivityChip = mOngoingCallController.hasOngoingCall();
}
+ boolean showSecondaryOngoingActivityChip =
+ Flags.statusBarScreenSharingChips()
+ && StatusBarRonChips.isEnabled()
+ && mHasSecondaryOngoingActivity;
return new StatusBarVisibilityModel(
showClock,
externalModel.getShowNotificationIcons(),
- showOngoingActivityChip && !headsUpVisible,
+ showPrimaryOngoingActivityChip && !headsUpVisible,
+ showSecondaryOngoingActivityChip && !headsUpVisible,
externalModel.getShowSystemInfo());
}
@@ -622,7 +641,7 @@
private void updateNotificationIconAreaAndOngoingActivityChip(boolean animate) {
StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility;
boolean disableNotifications = !visibilityModel.getShowNotificationIcons();
- boolean hasOngoingActivity = visibilityModel.getShowOngoingActivityChip();
+ boolean hasOngoingActivity = visibilityModel.getShowPrimaryOngoingActivityChip();
// Hide notifications if the disable flag is set or we have an ongoing activity.
if (disableNotifications || hasOngoingActivity) {
@@ -634,11 +653,23 @@
// Show the ongoing activity chip only if there is an ongoing activity *and* notification
// icons are allowed. (The ongoing activity chip occupies the same area as the notification,
// icons so if the icons are disabled then the activity chip should be, too.)
- boolean showOngoingActivityChip = hasOngoingActivity && !disableNotifications;
- if (showOngoingActivityChip) {
- showOngoingActivityChip(animate);
+ boolean showPrimaryOngoingActivityChip =
+ visibilityModel.getShowPrimaryOngoingActivityChip() && !disableNotifications;
+ if (showPrimaryOngoingActivityChip) {
+ showPrimaryOngoingActivityChip(animate);
} else {
- hideOngoingActivityChip(animate);
+ hidePrimaryOngoingActivityChip(animate);
+ }
+
+ boolean showSecondaryOngoingActivityChip =
+ // Secondary chips are only supported when RONs are enabled.
+ StatusBarRonChips.isEnabled()
+ && visibilityModel.getShowSecondaryOngoingActivityChip()
+ && !disableNotifications;
+ if (showSecondaryOngoingActivityChip) {
+ showSecondaryOngoingActivityChip(animate);
+ } else {
+ hideSecondaryOngoingActivityChip(animate);
}
}
@@ -729,20 +760,29 @@
animateShow(mClockView, animate);
}
- /** Hides the ongoing activity chip. */
- private void hideOngoingActivityChip(boolean animate) {
- animateHiddenState(mOngoingActivityChip, View.GONE, animate);
+ /** Hides the primary ongoing activity chip. */
+ private void hidePrimaryOngoingActivityChip(boolean animate) {
+ animateHiddenState(mPrimaryOngoingActivityChip, View.GONE, animate);
}
/**
- * Displays the ongoing activity chip.
+ * Displays the primary ongoing activity chip.
*
* If Flags.statusBarScreenSharingChips is disabled, this chip will only ever contain the
* ongoing call information, If that flag is enabled, it will support different kinds of ongoing
* activities. See b/332662551.
*/
- private void showOngoingActivityChip(boolean animate) {
- animateShow(mOngoingActivityChip, animate);
+ private void showPrimaryOngoingActivityChip(boolean animate) {
+ animateShow(mPrimaryOngoingActivityChip, animate);
+ }
+
+ private void hideSecondaryOngoingActivityChip(boolean animate) {
+ animateHiddenState(mSecondaryOngoingActivityChip, View.GONE, animate);
+ }
+
+ private void showSecondaryOngoingActivityChip(boolean animate) {
+ StatusBarRonChips.assertInNewMode();
+ animateShow(mSecondaryOngoingActivityChip, animate);
}
/**
@@ -850,7 +890,8 @@
private void initOngoingCallChip() {
mOngoingCallController.addCallback(mOngoingCallListener);
- mOngoingCallController.setChipView(mOngoingActivityChip);
+ // TODO(b/364653005): Do we also need to set the secondary activity chip?
+ mOngoingCallController.setChipView(mPrimaryOngoingActivityChip);
}
@Override
@@ -908,7 +949,8 @@
@Override
public void dump(PrintWriter printWriter, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */" ");
- pw.println("mHasOngoingActivity=" + mHasOngoingActivity);
+ pw.println("mHasPrimaryOngoingActivity=" + mHasPrimaryOngoingActivity);
+ pw.println("mHasSecondaryOngoingActivity=" + mHasSecondaryOngoingActivity);
pw.println("mAnimationsEnabled=" + mAnimationsEnabled);
StatusBarFragmentComponent component = mStatusBarFragmentComponent;
if (component == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
index 0a19023..deef886 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
@@ -16,16 +16,18 @@
package com.android.systemui.statusbar.phone.fragment
-import com.android.systemui.log.dagger.CollapsedSbFragmentLog
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.CollapsedSbFragmentLog
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
/** Used by [CollapsedStatusBarFragment] to log messages to a [LogBuffer]. */
-class CollapsedStatusBarFragmentLogger @Inject constructor(
- @CollapsedSbFragmentLog private val buffer: LogBuffer,
- private val disableFlagsLogger: DisableFlagsLogger,
+class CollapsedStatusBarFragmentLogger
+@Inject
+constructor(
+ @CollapsedSbFragmentLog private val buffer: LogBuffer,
+ private val disableFlagsLogger: DisableFlagsLogger,
) {
/**
@@ -38,17 +40,17 @@
new: DisableFlagsLogger.DisableState,
) {
buffer.log(
- TAG,
- LogLevel.INFO,
- {
- int1 = new.disable1
- int2 = new.disable2
- },
- {
- disableFlagsLogger.getDisableFlagsString(
- DisableFlagsLogger.DisableState(int1, int2),
- )
- }
+ TAG,
+ LogLevel.INFO,
+ {
+ int1 = new.disable1
+ int2 = new.disable2
+ },
+ {
+ disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(int1, int2),
+ )
+ }
)
}
@@ -59,13 +61,16 @@
{
bool1 = model.showClock
bool2 = model.showNotificationIcons
- bool3 = model.showOngoingActivityChip
+ bool3 = model.showPrimaryOngoingActivityChip
+ int1 = if (model.showSecondaryOngoingActivityChip) 1 else 0
bool4 = model.showSystemInfo
},
- { "New visibilities calculated internally. " +
+ {
+ "New visibilities calculated internally. " +
"showClock=$bool1 " +
"showNotificationIcons=$bool2 " +
- "showOngoingActivityChip=$bool3 " +
+ "showPrimaryOngoingActivityChip=$bool3 " +
+ "showSecondaryOngoingActivityChip=${if (int1 == 1) "true" else "false"}" +
"showSystemInfo=$bool4"
}
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
index 9255e63..e9e9a4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
@@ -26,9 +26,15 @@
data class StatusBarVisibilityModel(
val showClock: Boolean,
val showNotificationIcons: Boolean,
- val showOngoingActivityChip: Boolean,
+ val showPrimaryOngoingActivityChip: Boolean,
+ val showSecondaryOngoingActivityChip: Boolean,
val showSystemInfo: Boolean,
) {
+ fun isOngoingActivityStatusDifferentFrom(other: StatusBarVisibilityModel): Boolean {
+ return this.showPrimaryOngoingActivityChip != other.showPrimaryOngoingActivityChip ||
+ this.showSecondaryOngoingActivityChip != other.showSecondaryOngoingActivityChip
+ }
+
companion object {
/** Creates the default model. */
@JvmStatic
@@ -42,7 +48,8 @@
return StatusBarVisibilityModel(
showClock = false,
showNotificationIcons = false,
- showOngoingActivityChip = false,
+ showPrimaryOngoingActivityChip = false,
+ showSecondaryOngoingActivityChip = false,
showSystemInfo = false,
)
}
@@ -59,7 +66,8 @@
showNotificationIcons = (disabled1 and DISABLE_NOTIFICATION_ICONS) == 0,
// TODO(b/279899176): [CollapsedStatusBarFragment] always overwrites this with the
// value of [OngoingCallController]. Do we need to process the flag here?
- showOngoingActivityChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0,
+ showPrimaryOngoingActivityChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0,
+ showSecondaryOngoingActivityChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0,
showSystemInfo =
(disabled1 and DISABLE_SYSTEM_INFO) == 0 &&
(disabled2 and DISABLE2_SYSTEM_ICONS) == 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
index 8871dae..6c30330 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone.ui;
-import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.android.internal.statusbar.StatusBarIcon;
@@ -64,9 +63,8 @@
}
@Override
- protected LinearLayout.LayoutParams onCreateLayoutParams() {
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+ protected LinearLayout.LayoutParams onCreateLayoutParams(StatusBarIcon.Shape shape) {
+ LinearLayout.LayoutParams lp = super.onCreateLayoutParams(shape);
lp.setMargins(mIconHorizontalMargin, 0, mIconHorizontalMargin, 0);
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
index 5ad7376..91ead61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
@@ -20,6 +20,7 @@
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW;
+import static com.android.systemui.statusbar.phone.ui.StatusBarIconControllerImpl.usesModeIcons;
import android.annotation.Nullable;
import android.content.Context;
@@ -27,9 +28,8 @@
import android.view.ViewGroup;
import android.widget.LinearLayout;
-import androidx.annotation.VisibleForTesting;
-
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIcon.Shape;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.statusbar.BaseStatusBarFrameLayout;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -155,12 +155,11 @@
};
}
- @VisibleForTesting
protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
StatusBarIcon icon) {
StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
view.set(icon);
- mGroup.addView(view, index, onCreateLayoutParams());
+ mGroup.addView(view, index, onCreateLayoutParams(icon.shape));
return view;
}
@@ -174,7 +173,7 @@
int index) {
mBindableIcons.put(holder.getSlot(), holder);
ModernStatusBarView view = holder.getInitializer().createAndBind(mContext);
- mGroup.addView(view, index, onCreateLayoutParams());
+ mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT));
if (mIsInDemoMode) {
mDemoStatusIcons.addBindableIcon(holder);
}
@@ -183,7 +182,7 @@
protected StatusIconDisplayable addNewWifiIcon(int index, String slot) {
ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot);
- mGroup.addView(view, index, onCreateLayoutParams());
+ mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT));
if (mIsInDemoMode) {
mDemoStatusIcons.addModernWifiView(mWifiViewModel);
@@ -199,7 +198,7 @@
int subId
) {
BaseStatusBarFrameLayout view = onCreateModernStatusBarMobileView(slot, subId);
- mGroup.addView(view, index, onCreateLayoutParams());
+ mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT));
if (mIsInDemoMode) {
Context mobileContext = mMobileContextProvider
@@ -233,8 +232,12 @@
);
}
- protected LinearLayout.LayoutParams onCreateLayoutParams() {
- return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+ protected LinearLayout.LayoutParams onCreateLayoutParams(Shape shape) {
+ int width = usesModeIcons() && shape == StatusBarIcon.Shape.FIXED_SPACE
+ ? mIconSize
+ : ViewGroup.LayoutParams.WRAP_CONTENT;
+
+ return new LinearLayout.LayoutParams(width, mIconSize);
}
protected void destroy() {
@@ -256,6 +259,13 @@
/** Called once an icon has been set. */
public void onSetIcon(int viewIndex, StatusBarIcon icon) {
StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex);
+ if (usesModeIcons()) {
+ ViewGroup.LayoutParams current = view.getLayoutParams();
+ ViewGroup.LayoutParams desired = onCreateLayoutParams(icon.shape);
+ if (desired.width != current.width || desired.height != current.height) {
+ view.setLayoutParams(desired);
+ }
+ }
view.set(icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java
index ee528e9..0459b97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java
@@ -70,7 +70,8 @@
* @param preloadedIcon optional drawable corresponding to {@code iconResId}, if known
*/
void setResourceIcon(String slot, @Nullable String resPackage, @DrawableRes int iconResId,
- @Nullable Drawable preloadedIcon, CharSequence contentDescription);
+ @Nullable Drawable preloadedIcon, CharSequence contentDescription,
+ StatusBarIcon.Shape shape);
/**
* Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
index ad3a9e3..9b6d32b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
@@ -234,13 +234,14 @@
Icon.createWithResource(mContext, resourceId),
/* preloadedIcon= */ null,
contentDescription,
- StatusBarIcon.Type.SystemIcon);
+ StatusBarIcon.Type.SystemIcon,
+ StatusBarIcon.Shape.WRAP_CONTENT);
}
@Override
public void setResourceIcon(String slot, @Nullable String resPackage,
@DrawableRes int iconResId, @Nullable Drawable preloadedIcon,
- CharSequence contentDescription) {
+ CharSequence contentDescription, StatusBarIcon.Shape shape) {
if (!usesModeIcons()) {
Log.wtf("TAG",
"StatusBarIconController.setResourceIcon() should not be called without "
@@ -260,12 +261,13 @@
icon,
preloadedIcon,
contentDescription,
- StatusBarIcon.Type.ResourceIcon);
+ StatusBarIcon.Type.ResourceIcon,
+ shape);
}
private void setResourceIconInternal(String slot, Icon resourceIcon,
@Nullable Drawable preloadedIcon, CharSequence contentDescription,
- StatusBarIcon.Type type) {
+ StatusBarIcon.Type type, StatusBarIcon.Shape shape) {
checkArgument(resourceIcon.getType() == Icon.TYPE_RESOURCE,
"Expected Icon of TYPE_RESOURCE, but got " + resourceIcon.getType());
String resPackage = resourceIcon.getResPackage();
@@ -277,7 +279,7 @@
if (holder == null) {
StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, resPackage,
resourceIcon, /* iconLevel= */ 0, /* number=*/ 0,
- contentDescription, type);
+ contentDescription, type, shape);
icon.preloadedIcon = preloadedIcon;
holder = StatusBarIconHolder.fromIcon(icon);
setIcon(slot, holder);
@@ -286,6 +288,7 @@
holder.getIcon().icon = resourceIcon;
holder.getIcon().contentDescription = contentDescription;
holder.getIcon().type = type;
+ holder.getIcon().shape = shape;
holder.getIcon().preloadedIcon = preloadedIcon;
handleSet(slot, holder);
}
@@ -578,7 +581,7 @@
}
}
- private static boolean usesModeIcons() {
+ static boolean usesModeIcons() {
return android.app.Flags.modesApi() && android.app.Flags.modesUi()
&& android.app.Flags.modesUiIcons();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
index 85bbe7e..d601319 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
@@ -100,7 +100,14 @@
val showSpn = getBooleanExtra(EXTRA_SHOW_SPN, false)
val spn =
if (statusBarSwitchToSpnFromDataSpn()) {
- getStringExtra(EXTRA_SPN)
+ // Context: b/358669494. Use DATA_SPN if it exists, since that allows carriers to
+ // customize the display name. Otherwise, fall back to the SPN
+ val dataSpn = getStringExtra(EXTRA_DATA_SPN)
+ if (dataSpn.isNullOrEmpty()) {
+ getStringExtra(EXTRA_SPN)
+ } else {
+ dataSpn
+ }
} else {
getStringExtra(EXTRA_DATA_SPN)
}
@@ -109,10 +116,8 @@
val plmn = getStringExtra(EXTRA_PLMN)
val str = StringBuilder()
- val strData = StringBuilder()
if (showPlmn && plmn != null) {
str.append(plmn)
- strData.append(plmn)
}
if (showSpn && spn != null) {
if (str.isNotEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt
deleted file mode 100644
index cce3eb0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt
+++ /dev/null
@@ -1,31 +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.statusbar.pipeline.mobile.data.model
-
-import android.telephony.ServiceState
-
-/**
- * Simplified representation of a [ServiceState] for use in SystemUI. Add any fields that we need to
- * extract from service state here for consumption downstream
- */
-data class ServiceStateModel(val isEmergencyOnly: Boolean) {
- companion object {
- fun fromServiceState(serviceState: ServiceState): ServiceStateModel {
- return ServiceStateModel(isEmergencyOnly = serviceState.isEmergencyOnly)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index 5ad8bf1..32e9c85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -21,7 +21,6 @@
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileMappings.Config
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -93,17 +92,15 @@
val defaultMobileIconGroup: Flow<MobileIconGroup>
/**
- * [deviceServiceState] is equivalent to the last [Intent.ACTION_SERVICE_STATE] broadcast with a
- * subscriptionId of -1 (aka [SubscriptionManager.INVALID_SUBSCRIPTION_ID]).
+ * Can the device make emergency calls using the device-based service state? This field is only
+ * useful when all known active subscriptions are OOS and not emergency call capable.
*
- * While each [MobileConnectionsRepository] listens for the service state of each subscription,
- * there is potentially a service state associated with the device itself. This value can be
- * used to calculate e.g., the emergency calling capability of the device (as opposed to the
- * emergency calling capability of an individual mobile connection)
+ * Specifically, this checks every [ServiceState] of the device, and looks for any that report
+ * [ServiceState.isEmergencyOnly].
*
- * Note: this is a [StateFlow] using an eager sharing strategy.
+ * This is an eager flow, and re-evaluates whenever ACTION_SERVICE_STATE is sent for subId = -1.
*/
- val deviceServiceState: StateFlow<ServiceStateModel?>
+ val isDeviceEmergencyCallCapable: StateFlow<Boolean>
/**
* If any active SIM on the device is in
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index b068152..b247da4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -25,7 +25,6 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
@@ -152,16 +151,17 @@
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
- override val deviceServiceState: StateFlow<ServiceStateModel?> =
+ override val isDeviceEmergencyCallCapable: StateFlow<Boolean> =
activeRepo
- .flatMapLatest { it.deviceServiceState }
+ .flatMapLatest { it.isDeviceEmergencyCallCapable }
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- realRepository.deviceServiceState.value
+ realRepository.isDeviceEmergencyCallCapable.value
)
override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
+
override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure()
override val defaultDataSubId: StateFlow<Int> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index a944e91..3a79f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -27,7 +27,6 @@
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
@@ -137,10 +136,11 @@
override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
- // TODO(b/339023069): demo command for device-based connectivity state
- override val deviceServiceState: StateFlow<ServiceStateModel?> = MutableStateFlow(null)
+ // TODO(b/339023069): demo command for device-based emergency calls state
+ override val isDeviceEmergencyCallCapable: StateFlow<Boolean> = MutableStateFlow(false)
override val isAnySimSecure: Flow<Boolean> = flowOf(getIsAnySimSecure())
+
override fun getIsAnySimSecure(): Boolean = false
override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 261258a..b756a05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -21,7 +21,6 @@
import android.content.Intent
import android.content.IntentFilter
import android.telephony.CarrierConfigManager
-import android.telephony.ServiceState
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -49,7 +48,6 @@
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
@@ -72,7 +70,6 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
@@ -175,8 +172,8 @@
}
.flowOn(bgDispatcher)
- /** Note that this flow is eager, so we don't miss any state */
- override val deviceServiceState: StateFlow<ServiceStateModel?> =
+ /** Turn ACTION_SERVICE_STATE (for subId = -1) into an event */
+ private val serviceStateChangedEvent: Flow<Unit> =
broadcastDispatcher
.broadcastFlow(IntentFilter(Intent.ACTION_SERVICE_STATE)) { intent, _ ->
val subId =
@@ -185,24 +182,34 @@
INVALID_SUBSCRIPTION_ID
)
- val extras = intent.extras
- if (extras == null) {
- logger.logTopLevelServiceStateBroadcastMissingExtras(subId)
- return@broadcastFlow null
- }
-
- val serviceState = ServiceState.newFromBundle(extras)
- logger.logTopLevelServiceStateBroadcastEmergencyOnly(subId, serviceState)
+ // Only emit if the subId is not associated with an active subscription
if (subId == INVALID_SUBSCRIPTION_ID) {
- // Assume that -1 here is the device's service state. We don't care about
- // other ones.
- ServiceStateModel.fromServiceState(serviceState)
- } else {
- null
+ Unit
}
}
- .filterNotNull()
- .stateIn(scope, SharingStarted.Eagerly, null)
+ // Emit on start so that we always check the state at least once
+ .onStart { emit(Unit) }
+
+ /** Eager flow to determine the device-based emergency calls only state */
+ override val isDeviceEmergencyCallCapable: StateFlow<Boolean> =
+ serviceStateChangedEvent
+ .mapLatest {
+ val modems = telephonyManager.activeModemCount
+ // Check the service state for every modem. If any state reports emergency calling
+ // capable, then consider the device to have emergency call capabilities
+ (0..<modems)
+ .map { telephonyManager.getServiceStateForSlot(it) }
+ .any { it?.isEmergencyOnly == true }
+ }
+ .flowOn(bgDispatcher)
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogger,
+ columnPrefix = LOGGING_PREFIX,
+ columnName = "deviceEmergencyOnly",
+ initialValue = false,
+ )
+ .stateIn(scope, SharingStarted.Eagerly, false)
/**
* State flow that emits the set of mobile data subscriptions, each represented by its own
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 26553e6..28fff4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -385,15 +385,7 @@
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> =
- mobileConnectionsRepo.deviceServiceState
- .map { it?.isEmergencyOnly ?: false }
- .distinctUntilChanged()
- .logDiffsForTable(
- tableLogger,
- columnPrefix = LOGGING_PREFIX,
- columnName = "deviceEmergencyOnly",
- initialValue = false,
- )
+ mobileConnectionsRepo.isDeviceEmergencyCallCapable
/** Vends out new [MobileIconInteractor] for a particular subId */
override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index c24d694..49eabba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -18,28 +18,16 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
-import android.annotation.IdRes
-import android.content.res.ColorStateList
-import android.graphics.drawable.GradientDrawable
import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.Flags
-import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
-import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
+import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
-import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
import javax.inject.Inject
@@ -92,64 +80,53 @@
}
}
- if (Flags.statusBarScreenSharingChips()) {
- val chipView: View = view.requireViewById(R.id.ongoing_activity_chip)
- val chipContext = chipView.context
- val chipDefaultIconView: ImageView =
- chipView.requireViewById(R.id.ongoing_activity_chip_icon)
- val chipTimeView: ChipChronometer =
- chipView.requireViewById(R.id.ongoing_activity_chip_time)
- val chipTextView: TextView =
- chipView.requireViewById(R.id.ongoing_activity_chip_text)
- val chipBackgroundView =
- chipView.requireViewById<ChipBackgroundContainer>(
- R.id.ongoing_activity_chip_background
- )
+ if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) {
+ val primaryChipView: View =
+ view.requireViewById(R.id.ongoing_activity_chip_primary)
launch {
- viewModel.ongoingActivityChip.collect { chipModel ->
- when (chipModel) {
- is OngoingActivityChipModel.Shown -> {
- // Data
- setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
- setChipMainContent(chipModel, chipTextView, chipTimeView)
- chipView.setOnClickListener(chipModel.onClickListener)
- updateChipPadding(
- chipModel,
- chipBackgroundView,
- chipTextView,
- chipTimeView,
- )
-
- // Accessibility
- setChipAccessibility(chipModel, chipView, chipBackgroundView)
-
- // Colors
- val textColor = chipModel.colors.text(chipContext)
- chipTimeView.setTextColor(textColor)
- chipTextView.setTextColor(textColor)
- (chipBackgroundView.background as GradientDrawable).color =
- chipModel.colors.background(chipContext)
-
- // Notify listeners
+ viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
+ OngoingActivityChipBinder.bind(primaryChipModel, primaryChipView)
+ when (primaryChipModel) {
+ is OngoingActivityChipModel.Shown ->
listener.onOngoingActivityStatusChanged(
- hasOngoingActivity = true,
+ hasPrimaryOngoingActivity = true,
+ hasSecondaryOngoingActivity = false,
shouldAnimate = true,
)
- }
- is OngoingActivityChipModel.Hidden -> {
- // The Chronometer should be stopped to prevent leaks -- see
- // b/192243808 and [Chronometer.start].
- chipTimeView.stop()
+ is OngoingActivityChipModel.Hidden ->
listener.onOngoingActivityStatusChanged(
- hasOngoingActivity = false,
- shouldAnimate = chipModel.shouldAnimate,
+ hasPrimaryOngoingActivity = false,
+ hasSecondaryOngoingActivity = false,
+ shouldAnimate = primaryChipModel.shouldAnimate,
)
- }
}
}
}
}
+ if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) {
+ val primaryChipView: View =
+ view.requireViewById(R.id.ongoing_activity_chip_primary)
+ val secondaryChipView: View =
+ view.requireViewById(R.id.ongoing_activity_chip_secondary)
+ launch {
+ viewModel.ongoingActivityChips.collect { chips ->
+ OngoingActivityChipBinder.bind(chips.primary, primaryChipView)
+ // TODO(b/364653005): Don't show the secondary chip if there isn't
+ // enough space for it.
+ OngoingActivityChipBinder.bind(chips.secondary, secondaryChipView)
+ listener.onOngoingActivityStatusChanged(
+ hasPrimaryOngoingActivity =
+ chips.primary is OngoingActivityChipModel.Shown,
+ hasSecondaryOngoingActivity =
+ chips.secondary is OngoingActivityChipModel.Shown,
+ // TODO(b/364653005): Figure out the animation story here.
+ shouldAnimate = true,
+ )
+ }
+ }
+ }
+
if (SceneContainerFlag.isEnabled) {
launch {
viewModel.isHomeStatusBarAllowedByScene.collect {
@@ -161,240 +138,6 @@
}
}
- private fun setChipIcon(
- chipModel: OngoingActivityChipModel.Shown,
- backgroundView: ChipBackgroundContainer,
- defaultIconView: ImageView,
- ) {
- // Always remove any previously set custom icon. If we have a new custom icon, we'll re-add
- // it.
- backgroundView.removeView(backgroundView.getCustomIconView())
-
- val iconTint = chipModel.colors.text(defaultIconView.context)
-
- when (val icon = chipModel.icon) {
- null -> {
- defaultIconView.visibility = View.GONE
- }
- is OngoingActivityChipModel.ChipIcon.SingleColorIcon -> {
- IconViewBinder.bind(icon.impl, defaultIconView)
- defaultIconView.visibility = View.VISIBLE
- defaultIconView.tintView(iconTint)
- }
- is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
- StatusBarRonChips.assertInNewMode()
- IconViewBinder.bind(icon.impl, defaultIconView)
- defaultIconView.visibility = View.VISIBLE
- defaultIconView.untintView()
- }
- is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
- // Hide the default icon since we'll show this custom icon instead.
- defaultIconView.visibility = View.GONE
-
- // Add the new custom icon:
- // 1. Set up the right visual params.
- val iconView = icon.impl
- with(iconView) {
- id = CUSTOM_ICON_VIEW_ID
- // TODO(b/354930838): Update the content description to not include "phone" and
- // maybe include the app name.
- contentDescription =
- context.resources.getString(R.string.ongoing_phone_call_content_description)
- tintView(iconTint)
- }
-
- // 2. If we just reinflated the view, we may need to detach the icon view from the
- // old chip before we reattach it to the new one.
- // See also: NotificationIconContainerViewBinder#bindIcons.
- val currentParent = iconView.parent as? ViewGroup
- if (currentParent != null && currentParent != backgroundView) {
- currentParent.removeView(iconView)
- currentParent.removeTransientView(iconView)
- }
-
- // 3: Add the icon as the starting view.
- backgroundView.addView(
- iconView,
- /* index= */ 0,
- generateCustomIconLayoutParams(iconView),
- )
- }
- }
- }
-
- private fun View.getCustomIconView(): StatusBarIconView? {
- return this.findViewById(CUSTOM_ICON_VIEW_ID)
- }
-
- private fun ImageView.tintView(color: Int) {
- this.imageTintList = ColorStateList.valueOf(color)
- }
-
- private fun ImageView.untintView() {
- this.imageTintList = null
- }
-
- private fun generateCustomIconLayoutParams(iconView: ImageView): FrameLayout.LayoutParams {
- val customIconSize =
- iconView.context.resources.getDimensionPixelSize(
- R.dimen.ongoing_activity_chip_embedded_padding_icon_size
- )
- return FrameLayout.LayoutParams(customIconSize, customIconSize)
- }
-
- private fun setChipMainContent(
- chipModel: OngoingActivityChipModel.Shown,
- chipTextView: TextView,
- chipTimeView: ChipChronometer,
- ) {
- when (chipModel) {
- is OngoingActivityChipModel.Shown.Countdown -> {
- chipTextView.text = chipModel.secondsUntilStarted.toString()
- chipTextView.visibility = View.VISIBLE
-
- chipTimeView.hide()
- }
- is OngoingActivityChipModel.Shown.Text -> {
- chipTextView.text = chipModel.text
- chipTextView.visibility = View.VISIBLE
-
- chipTimeView.hide()
- }
- is OngoingActivityChipModel.Shown.Timer -> {
- ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
- chipTimeView.visibility = View.VISIBLE
-
- chipTextView.visibility = View.GONE
- }
- is OngoingActivityChipModel.Shown.IconOnly -> {
- chipTextView.visibility = View.GONE
- chipTimeView.hide()
- }
- }
- }
-
- private fun ChipChronometer.hide() {
- // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
- // [Chronometer.start].
- this.stop()
- this.visibility = View.GONE
- }
-
- private fun updateChipPadding(
- chipModel: OngoingActivityChipModel.Shown,
- backgroundView: View,
- chipTextView: TextView,
- chipTimeView: ChipChronometer,
- ) {
- if (chipModel.icon != null) {
- if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
- // If the icon is a custom [StatusBarIconView], then it should've come from
- // `Notification.smallIcon`, which is required to embed its own paddings. We need to
- // adjust the other paddings to make everything look good :)
- backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon()
- chipTextView.setTextPaddingForEmbeddedPaddingIcon()
- chipTimeView.setTextPaddingForEmbeddedPaddingIcon()
- } else {
- backgroundView.setBackgroundPaddingForNormalIcon()
- chipTextView.setTextPaddingForNormalIcon()
- chipTimeView.setTextPaddingForNormalIcon()
- }
- } else {
- backgroundView.setBackgroundPaddingForNoIcon()
- chipTextView.setTextPaddingForNoIcon()
- chipTimeView.setTextPaddingForNoIcon()
- }
- }
-
- private fun View.setTextPaddingForEmbeddedPaddingIcon() {
- val newPaddingEnd =
- context.resources.getDimensionPixelSize(
- R.dimen.ongoing_activity_chip_text_end_padding_for_embedded_padding_icon
- )
- setPaddingRelative(
- // The icon should embed enough padding between the icon and time view.
- /* start= */ 0,
- this.paddingTop,
- newPaddingEnd,
- this.paddingBottom,
- )
- }
-
- private fun View.setTextPaddingForNormalIcon() {
- this.setPaddingRelative(
- this.context.resources.getDimensionPixelSize(
- R.dimen.ongoing_activity_chip_icon_text_padding
- ),
- paddingTop,
- // The background view will contain the right end padding.
- /* end= */ 0,
- paddingBottom,
- )
- }
-
- private fun View.setTextPaddingForNoIcon() {
- // The background view will have even start & end paddings, so we don't want the text view
- // to add any additional padding.
- this.setPaddingRelative(/* start= */ 0, paddingTop, /* end= */ 0, paddingBottom)
- }
-
- private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() {
- val sidePadding =
- context.resources.getDimensionPixelSize(
- R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
- )
- setPaddingRelative(
- sidePadding,
- paddingTop,
- sidePadding,
- paddingBottom,
- )
- }
-
- private fun View.setBackgroundPaddingForNormalIcon() {
- val sidePadding =
- context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding)
- setPaddingRelative(
- sidePadding,
- paddingTop,
- sidePadding,
- paddingBottom,
- )
- }
-
- private fun View.setBackgroundPaddingForNoIcon() {
- // The padding for the normal icon is also appropriate for no icon.
- setBackgroundPaddingForNormalIcon()
- }
-
- private fun setChipAccessibility(
- chipModel: OngoingActivityChipModel.Shown,
- chipView: View,
- chipBackgroundView: View,
- ) {
- when (chipModel) {
- is OngoingActivityChipModel.Shown.Countdown -> {
- // Set as assertive so talkback will announce the countdown
- chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
- }
- is OngoingActivityChipModel.Shown.Timer,
- is OngoingActivityChipModel.Shown.Text,
- is OngoingActivityChipModel.Shown.IconOnly -> {
- chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
- }
- }
- // Clickable chips need to be a minimum size for accessibility purposes, but let
- // non-clickable chips be smaller.
- if (chipModel.onClickListener != null) {
- chipBackgroundView.minimumWidth =
- chipBackgroundView.context.resources.getDimensionPixelSize(
- R.dimen.min_clickable_item_size
- )
- } else {
- chipBackgroundView.minimumWidth = 0
- }
- }
-
private fun animateLightsOutView(view: View, visible: Boolean) {
view.animate().cancel()
@@ -424,10 +167,6 @@
)
.start()
}
-
- companion object {
- @IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon
- }
}
/** Listener for various events that may affect the status bar's visibility. */
@@ -447,7 +186,11 @@
* @param shouldAnimate true if the chip should animate in/out, and false if the chip should
* immediately appear/disappear.
*/
- fun onOngoingActivityStatusChanged(hasOngoingActivity: Boolean, shouldAnimate: Boolean)
+ fun onOngoingActivityStatusChanged(
+ hasPrimaryOngoingActivity: Boolean,
+ hasSecondaryOngoingActivity: Boolean,
+ shouldAnimate: Boolean,
+ )
/**
* Called when the scene state has changed such that the home status bar is newly allowed or no
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index d6c3834..9cce2b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -27,6 +27,7 @@
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -64,8 +65,17 @@
/** Emits whenever a transition from lockscreen to dream has started. */
val transitionFromLockscreenToDreamStartedEvent: Flow<Unit>
- /** The ongoing activity chip that should be shown on the left-hand side of the status bar. */
- val ongoingActivityChip: StateFlow<OngoingActivityChipModel>
+ /**
+ * The ongoing activity chip that should be primarily shown on the left-hand side of the status
+ * bar. If there are multiple ongoing activity chips, this one should take priority.
+ */
+ val primaryOngoingActivityChip: StateFlow<OngoingActivityChipModel>
+
+ /**
+ * The multiple ongoing activity chips that should be shown on the left-hand side of the status
+ * bar.
+ */
+ val ongoingActivityChips: StateFlow<MultipleOngoingActivityChipsModel>
/**
* True if the current scene can show the home status bar (aka this status bar), and false if
@@ -108,7 +118,9 @@
.filter { it.transitionState == TransitionState.STARTED }
.map {}
- override val ongoingActivityChip = ongoingActivityChipsViewModel.chip
+ override val primaryOngoingActivityChip = ongoingActivityChipsViewModel.primaryChip
+
+ override val ongoingActivityChips = ongoingActivityChipsViewModel.chips
override val isHomeStatusBarAllowedByScene: StateFlow<Boolean> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index fc7a672..bc7d376 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -64,9 +64,6 @@
const val COL_NAME_IS_ENABLED = "isEnabled"
/** Column name to use for [isWifiDefault] for table logging. */
const val COL_NAME_IS_DEFAULT = "isDefault"
-
- const val CARRIER_MERGED_INVALID_SUB_ID_REASON =
- "Wifi network was carrier merged but had invalid sub ID"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index 152d181..f4bb1a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -46,7 +46,7 @@
private val _isWifiDefault = MutableStateFlow(false)
override val isWifiDefault: StateFlow<Boolean> = _isWifiDefault
- private val _wifiNetwork = MutableStateFlow<WifiNetworkModel>(WifiNetworkModel.Inactive)
+ private val _wifiNetwork = MutableStateFlow<WifiNetworkModel>(WifiNetworkModel.Inactive())
override val wifiNetwork: StateFlow<WifiNetworkModel> = _wifiNetwork
private val _secondaryNetworks = MutableStateFlow<List<WifiNetworkModel>>(emptyList())
@@ -82,7 +82,7 @@
_isWifiEnabled.value = false
_isWifiDefault.value = false
_wifiActivity.value = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
- _wifiNetwork.value = WifiNetworkModel.Inactive
+ _wifiNetwork.value = WifiNetworkModel.Inactive()
}
private fun processEnabledWifiState(event: FakeWifiEventModel.Wifi) {
@@ -100,30 +100,21 @@
}
private fun FakeWifiEventModel.Wifi.toWifiNetworkModel(): WifiNetworkModel =
- WifiNetworkModel.Active(
- networkId = DEMO_NET_ID,
+ WifiNetworkModel.Active.of(
isValidated = validated ?: true,
level = level ?: 0,
ssid = ssid ?: DEMO_NET_SSID,
hotspotDeviceType = hotspotDeviceType,
-
- // These fields below aren't supported in demo mode, since they aren't needed to satisfy
- // the interface.
- isPasspointAccessPoint = false,
- isOnlineSignUpForPasspointAccessPoint = false,
- passpointProviderFriendlyName = null,
)
private fun FakeWifiEventModel.CarrierMerged.toCarrierMergedModel(): WifiNetworkModel =
- WifiNetworkModel.CarrierMerged(
- networkId = DEMO_NET_ID,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = subscriptionId,
level = level,
numberOfLevels = numberOfLevels,
)
companion object {
- private const val DEMO_NET_ID = 1234
private const val DEMO_NET_SSID = "Demo SSID"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index 885abca..76024cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -19,7 +19,6 @@
import android.annotation.SuppressLint
import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
-import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
@@ -29,8 +28,6 @@
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.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.table.TableLogBuffer
@@ -41,18 +38,14 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.CARRIER_MERGED_INVALID_SUB_ID_REASON
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.COL_NAME_IS_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.COL_NAME_IS_ENABLED
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Inactive.toHotspotDeviceType
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Unavailable.toHotspotDeviceType
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.wifitrackerlib.HotspotNetworkEntry
import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
-import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX
-import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN
-import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE
import com.android.wifitrackerlib.WifiPickerTracker
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -75,7 +68,6 @@
class WifiRepositoryImpl
@Inject
constructor(
- featureFlags: FeatureFlags,
@Application private val scope: CoroutineScope,
@Main private val mainExecutor: Executor,
@Background private val bgDispatcher: CoroutineDispatcher,
@@ -90,8 +82,6 @@
mainExecutor.execute { it.currentState = Lifecycle.State.CREATED }
}
- private val isInstantTetherEnabled = featureFlags.isEnabled(Flags.INSTANT_TETHER)
-
private var wifiPickerTracker: WifiPickerTracker? = null
private val wifiPickerTrackerInfo: StateFlow<WifiPickerTrackerInfo> = run {
@@ -109,16 +99,11 @@
val connectedEntry = wifiPickerTracker.mergedOrPrimaryConnection
logOnWifiEntriesChanged(connectedEntry)
+ val activeNetworks = wifiPickerTracker?.activeWifiEntries ?: emptyList()
val secondaryNetworks =
- if (featureFlags.isEnabled(Flags.WIFI_SECONDARY_NETWORKS)) {
- val activeNetworks =
- wifiPickerTracker?.activeWifiEntries ?: emptyList()
- activeNetworks
- .filter { it != connectedEntry && !it.isPrimaryNetwork }
- .map { it.toWifiNetworkModel() }
- } else {
- emptyList()
- }
+ activeNetworks
+ .filter { it != connectedEntry && !it.isPrimaryNetwork }
+ .map { it.toWifiNetworkModel() }
// [WifiPickerTracker.connectedWifiEntry] will return the same instance
// but with updated internals. For example, when its validation status
@@ -130,7 +115,8 @@
// into our internal model immediately. [toWifiNetworkModel] always
// returns a new instance, so the flow is guaranteed to emit.
send(
- newPrimaryNetwork = connectedEntry?.toPrimaryWifiNetworkModel()
+ newPrimaryNetwork =
+ connectedEntry?.toPrimaryWifiNetworkModel()
?: WIFI_NETWORK_DEFAULT,
newSecondaryNetworks = secondaryNetworks,
newIsDefault = connectedEntry?.isDefaultNetwork ?: false,
@@ -255,48 +241,40 @@
}
private fun MergedCarrierEntry.convertCarrierMergedToModel(): WifiNetworkModel {
- return if (this.subscriptionId == INVALID_SUBSCRIPTION_ID) {
- WifiNetworkModel.Invalid(CARRIER_MERGED_INVALID_SUB_ID_REASON)
- } else {
- WifiNetworkModel.CarrierMerged(
- networkId = NETWORK_ID,
- subscriptionId = this.subscriptionId,
- level = this.level,
- // WifiManager APIs to calculate the signal level start from 0, so
- // maxSignalLevel + 1 represents the total level buckets count.
- numberOfLevels = wifiManager.maxSignalLevel + 1,
- )
- }
+ // WifiEntry instance values aren't guaranteed to be stable between method calls
+ // because
+ // WifiPickerTracker is continuously updating the same object. Save the level in a
+ // local
+ // variable so that checking the level validity here guarantees that the level will
+ // still be
+ // valid when we create the `WifiNetworkModel.Active` instance later. Otherwise, the
+ // level
+ // could be valid here but become invalid later, and `WifiNetworkModel.Active` will
+ // throw
+ // an exception. See b/362384551.
+
+ return WifiNetworkModel.CarrierMerged.of(
+ subscriptionId = this.subscriptionId,
+ level = this.level,
+ // WifiManager APIs to calculate the signal level start from 0, so
+ // maxSignalLevel + 1 represents the total level buckets count.
+ numberOfLevels = wifiManager.maxSignalLevel + 1,
+ )
}
private fun WifiEntry.convertNormalToModel(): WifiNetworkModel {
- if (this.level == WIFI_LEVEL_UNREACHABLE || this.level !in WIFI_LEVEL_MIN..WIFI_LEVEL_MAX) {
- // If our level means the network is unreachable or the level is otherwise invalid, we
- // don't have an active network.
- return WifiNetworkModel.Inactive
- }
-
val hotspotDeviceType =
- if (isInstantTetherEnabled && this is HotspotNetworkEntry) {
+ if (this is HotspotNetworkEntry) {
this.deviceType.toHotspotDeviceType()
} else {
WifiNetworkModel.HotspotDeviceType.NONE
}
- return WifiNetworkModel.Active(
- networkId = NETWORK_ID,
+ return WifiNetworkModel.Active.of(
isValidated = this.hasInternetAccess(),
level = this.level,
ssid = this.title,
hotspotDeviceType = hotspotDeviceType,
- // With WifiTrackerLib, [WifiEntry.title] will appropriately fetch the SSID for
- // typical wifi networks *and* passpoint/OSU APs. So, the AP-specific values can
- // always be false/null in this repository.
- // TODO(b/292534484): Remove these fields from the wifi network model once this
- // repository is fully enabled.
- isPasspointAccessPoint = false,
- isOnlineSignUpForPasspointAccessPoint = false,
- passpointProviderFriendlyName = null,
)
}
@@ -408,7 +386,6 @@
class Factory
@Inject
constructor(
- private val featureFlags: FeatureFlags,
@Application private val scope: CoroutineScope,
@Main private val mainExecutor: Executor,
@Background private val bgDispatcher: CoroutineDispatcher,
@@ -418,7 +395,6 @@
) {
fun create(wifiManager: WifiManager): WifiRepositoryImpl {
return WifiRepositoryImpl(
- featureFlags,
scope,
mainExecutor,
bgDispatcher,
@@ -432,26 +408,12 @@
companion object {
// Start out with no known wifi network.
- @VisibleForTesting val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
+ @VisibleForTesting val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive()
private const val WIFI_STATE_DEFAULT = WifiManager.WIFI_STATE_DISABLED
val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
private const val TAG = "WifiTrackerLibInputLog"
-
- /**
- * [WifiNetworkModel.Active.networkId] is only used at the repository layer. It's used by
- * [WifiRepositoryImpl], which tracks the ID in order to correctly apply the framework
- * callbacks within the repository.
- *
- * Since this class does not need to manually apply framework callbacks and since the
- * network ID is not used beyond the repository, it's safe to use an invalid ID in this
- * repository.
- *
- * The [WifiNetworkModel.Active.networkId] field should be deleted once we've fully migrated
- * to [WifiRepositoryImpl].
- */
- private const val NETWORK_ID = -1
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 110e339..c0b0c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -83,8 +83,6 @@
is WifiNetworkModel.CarrierMerged -> null
is WifiNetworkModel.Active ->
when {
- info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
- info.passpointProviderFriendlyName
info.hasValidSsid() -> info.ssid
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
index 7078a2e..3220377 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.wifi.shared.model
+import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.UNKNOWN_SSID
import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
import android.telephony.SubscriptionManager
@@ -23,7 +24,12 @@
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.isValid
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.of
import com.android.wifitrackerlib.HotspotNetworkEntry.DeviceType
+import com.android.wifitrackerlib.WifiEntry
+import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE
/** Provides information about the current wifi network. */
sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
@@ -38,6 +44,7 @@
*/
object Unavailable : WifiNetworkModel() {
override fun toString() = "WifiNetwork.Unavailable"
+
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
if (prevVal is Unavailable) {
return
@@ -48,16 +55,12 @@
override fun logFull(row: TableRowLogger) {
row.logChange(COL_NETWORK_TYPE, TYPE_UNAVAILABLE)
- row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
row.logChange(COL_VALIDATED, false)
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
row.logChange(COL_SSID, null)
row.logChange(COL_HOTSPOT, null)
- row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
- row.logChange(COL_ONLINE_SIGN_UP, false)
- row.logChange(COL_PASSPOINT_NAME, null)
}
}
@@ -66,7 +69,8 @@
/** A description of why the wifi information was invalid. */
val invalidReason: String,
) : WifiNetworkModel() {
- override fun toString() = "WifiNetwork.Invalid[$invalidReason]"
+ override fun toString() = "WifiNetwork.Invalid[reason=$invalidReason]"
+
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
if (prevVal !is Invalid) {
logFull(row)
@@ -74,50 +78,47 @@
}
if (invalidReason != prevVal.invalidReason) {
- row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE[reason=$invalidReason]")
}
}
override fun logFull(row: TableRowLogger) {
- row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
- row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE[reason=$invalidReason]")
row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
row.logChange(COL_VALIDATED, false)
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
row.logChange(COL_SSID, null)
row.logChange(COL_HOTSPOT, null)
- row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
- row.logChange(COL_ONLINE_SIGN_UP, false)
- row.logChange(COL_PASSPOINT_NAME, null)
}
}
/** A model representing that we have no active wifi network. */
- object Inactive : WifiNetworkModel() {
- override fun toString() = "WifiNetwork.Inactive"
+ data class Inactive(
+ /** An optional description of why the wifi information was inactive. */
+ val inactiveReason: String? = null,
+ ) : WifiNetworkModel() {
+ override fun toString() = "WifiNetwork.Inactive[reason=$inactiveReason]"
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
- if (prevVal is Inactive) {
+ if (prevVal !is Inactive) {
+ logFull(row)
return
}
- // When changing to Inactive, we need to log diffs to all the fields.
- logFull(row)
+ if (inactiveReason != prevVal.inactiveReason) {
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_INACTIVE[reason=$inactiveReason]")
+ }
}
override fun logFull(row: TableRowLogger) {
- row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
- row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_INACTIVE[reason=$inactiveReason]")
row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
row.logChange(COL_VALIDATED, false)
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
row.logChange(COL_SSID, null)
row.logChange(COL_HOTSPOT, null)
- row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
- row.logChange(COL_ONLINE_SIGN_UP, false)
- row.logChange(COL_PASSPOINT_NAME, null)
}
}
@@ -126,38 +127,71 @@
* treated as more of a mobile network.
*
* See [android.net.wifi.WifiInfo.isCarrierMerged] for more information.
+ *
+ * IMPORTANT: Do *not* call [copy] on this class. Instead, use the factory [of] methods. [of]
+ * will verify preconditions correctly.
*/
- data class CarrierMerged(
- /**
- * The [android.net.Network.netId] we received from
- * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
- *
- * Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId].
- */
- val networkId: Int,
-
+ data class CarrierMerged
+ private constructor(
/**
* The subscription ID that this connection represents.
*
* Comes from [android.net.wifi.WifiInfo.getSubscriptionId].
*
- * Per that method, this value must not be [INVALID_SUBSCRIPTION_ID] (if it was invalid,
- * then this is *not* a carrier merged network).
+ * Per that method, this value must not be [SubscriptionManager.INVALID_SUBSCRIPTION_ID] (if
+ * it was invalid, then this is *not* a carrier merged network).
*/
val subscriptionId: Int,
- /** The signal level, guaranteed to be 0 <= level <= numberOfLevels. */
+ /** The signal level, required to be 0 <= level <= numberOfLevels. */
val level: Int,
/** The maximum possible level. */
- val numberOfLevels: Int = MobileConnectionRepository.DEFAULT_NUM_LEVELS,
+ val numberOfLevels: Int,
) : WifiNetworkModel() {
- init {
- require(level in MIN_VALID_LEVEL..numberOfLevels) {
- "0 <= wifi level <= $numberOfLevels required; level was $level"
+ companion object {
+ /**
+ * Creates a [CarrierMerged] instance, or an [Invalid] instance if any of the arguments
+ * are invalid.
+ */
+ fun of(
+ subscriptionId: Int,
+ level: Int,
+ numberOfLevels: Int = MobileConnectionRepository.DEFAULT_NUM_LEVELS
+ ): WifiNetworkModel {
+ if (!subscriptionId.isSubscriptionIdValid()) {
+ return Invalid(INVALID_SUB_ID_ERROR_STRING)
+ }
+ if (!level.isLevelValid(numberOfLevels)) {
+ return Invalid(getInvalidLevelErrorString(level, numberOfLevels))
+ }
+ return CarrierMerged(subscriptionId, level, numberOfLevels)
}
- require(subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- "subscription ID cannot be invalid"
+
+ private fun Int.isLevelValid(maxLevel: Int): Boolean {
+ return this != WIFI_LEVEL_UNREACHABLE && this in MIN_VALID_LEVEL..maxLevel
+ }
+
+ private fun getInvalidLevelErrorString(level: Int, maxLevel: Int): String {
+ return "Wifi network was carrier merged but had invalid level. " +
+ "$MIN_VALID_LEVEL <= wifi level <= $maxLevel required; " +
+ "level was $level"
+ }
+
+ private fun Int.isSubscriptionIdValid(): Boolean {
+ return this != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ }
+
+ private const val INVALID_SUB_ID_ERROR_STRING =
+ "Wifi network was carrier merged but had invalid sub ID"
+ }
+
+ init {
+ require(level.isLevelValid(numberOfLevels)) {
+ "${getInvalidLevelErrorString(level, numberOfLevels)}. $DO_NOT_USE_COPY_ERROR"
+ }
+ require(subscriptionId.isSubscriptionIdValid()) {
+ "$INVALID_SUB_ID_ERROR_STRING. $DO_NOT_USE_COPY_ERROR"
}
}
@@ -167,9 +201,6 @@
return
}
- if (prevVal.networkId != networkId) {
- row.logChange(COL_NETWORK_ID, networkId)
- }
if (prevVal.subscriptionId != subscriptionId) {
row.logChange(COL_SUB_ID, subscriptionId)
}
@@ -183,56 +214,72 @@
override fun logFull(row: TableRowLogger) {
row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
- row.logChange(COL_NETWORK_ID, networkId)
row.logChange(COL_SUB_ID, subscriptionId)
row.logChange(COL_VALIDATED, true)
row.logChange(COL_LEVEL, level)
row.logChange(COL_NUM_LEVELS, numberOfLevels)
row.logChange(COL_SSID, null)
row.logChange(COL_HOTSPOT, null)
- row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
- row.logChange(COL_ONLINE_SIGN_UP, false)
- row.logChange(COL_PASSPOINT_NAME, null)
}
}
- /** Provides information about an active wifi network. */
- data class Active(
- /**
- * The [android.net.Network.netId] we received from
- * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
- *
- * Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId].
- */
- val networkId: Int,
-
+ /**
+ * Provides information about an active wifi network.
+ *
+ * IMPORTANT: Do *not* call [copy] on this class. Instead, use the factory [of] method. [of]
+ * will verify preconditions correctly.
+ */
+ data class Active
+ private constructor(
/** See [android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED]. */
- val isValidated: Boolean = false,
+ val isValidated: Boolean,
- /** The wifi signal level, guaranteed to be 0 <= level <= 4. */
+ /** The wifi signal level, required to be 0 <= level <= 4. */
val level: Int,
/** See [android.net.wifi.WifiInfo.ssid]. */
- val ssid: String? = null,
+ val ssid: String?,
/**
* The type of device providing a hotspot connection, or [HotspotDeviceType.NONE] if this
* isn't a hotspot connection.
*/
- val hotspotDeviceType: HotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
-
- /** See [android.net.wifi.WifiInfo.isPasspointAp]. */
- val isPasspointAccessPoint: Boolean = false,
-
- /** See [android.net.wifi.WifiInfo.isOsuAp]. */
- val isOnlineSignUpForPasspointAccessPoint: Boolean = false,
-
- /** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */
- val passpointProviderFriendlyName: String? = null,
+ val hotspotDeviceType: HotspotDeviceType,
) : WifiNetworkModel() {
+ companion object {
+ /**
+ * Creates an [Active] instance, or an [Inactive] instance if any of the arguments are
+ * invalid.
+ */
+ @JvmStatic
+ fun of(
+ isValidated: Boolean = false,
+ level: Int,
+ ssid: String? = null,
+ hotspotDeviceType: HotspotDeviceType = HotspotDeviceType.NONE,
+ ): WifiNetworkModel {
+ if (!level.isValid()) {
+ return Inactive(getInvalidLevelErrorString(level))
+ }
+ return Active(isValidated, level, ssid, hotspotDeviceType)
+ }
+
+ private fun Int.isValid(): Boolean {
+ return this != WIFI_LEVEL_UNREACHABLE && this in MIN_VALID_LEVEL..MAX_VALID_LEVEL
+ }
+
+ private fun getInvalidLevelErrorString(level: Int): String {
+ return "Wifi network was active but had invalid level. " +
+ "$MIN_VALID_LEVEL <= wifi level <= $MAX_VALID_LEVEL required; " +
+ "level was $level"
+ }
+
+ @VisibleForTesting internal const val MAX_VALID_LEVEL = WifiEntry.WIFI_LEVEL_MAX
+ }
+
init {
- require(level in MIN_VALID_LEVEL..MAX_VALID_LEVEL) {
- "0 <= wifi level <= 4 required; level was $level"
+ require(level.isValid()) {
+ "${getInvalidLevelErrorString(level)}. $DO_NOT_USE_COPY_ERROR"
}
}
@@ -247,9 +294,6 @@
return
}
- if (prevVal.networkId != networkId) {
- row.logChange(COL_NETWORK_ID, networkId)
- }
if (prevVal.isValidated != isValidated) {
row.logChange(COL_VALIDATED, isValidated)
}
@@ -262,68 +306,21 @@
if (prevVal.hotspotDeviceType != hotspotDeviceType) {
row.logChange(COL_HOTSPOT, hotspotDeviceType.name)
}
-
- // TODO(b/238425913): The passpoint-related values are frequently never used, so it
- // would be great to not log them when they're not used.
- if (prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
- row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
- }
- if (
- prevVal.isOnlineSignUpForPasspointAccessPoint !=
- isOnlineSignUpForPasspointAccessPoint
- ) {
- row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
- }
- if (prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
- row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
- }
}
override fun logFull(row: TableRowLogger) {
row.logChange(COL_NETWORK_TYPE, TYPE_ACTIVE)
- row.logChange(COL_NETWORK_ID, networkId)
row.logChange(COL_SUB_ID, null)
row.logChange(COL_VALIDATED, isValidated)
row.logChange(COL_LEVEL, level)
row.logChange(COL_NUM_LEVELS, null)
row.logChange(COL_SSID, ssid)
row.logChange(COL_HOTSPOT, hotspotDeviceType.name)
- row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
- row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
- row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
- }
-
- override fun toString(): String {
- // Only include the passpoint-related values in the string if we have them. (Most
- // networks won't have them so they'll be mostly clutter.)
- val passpointString =
- if (
- isPasspointAccessPoint ||
- isOnlineSignUpForPasspointAccessPoint ||
- passpointProviderFriendlyName != null
- ) {
- ", isPasspointAp=$isPasspointAccessPoint, " +
- "isOnlineSignUpForPasspointAp=$isOnlineSignUpForPasspointAccessPoint, " +
- "passpointName=$passpointProviderFriendlyName"
- } else {
- ""
- }
-
- return "WifiNetworkModel.Active(networkId=$networkId, isValidated=$isValidated, " +
- "level=$level, ssid=$ssid$passpointString)"
- }
-
- companion object {
- // TODO(b/292534484): Use [com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX] instead
- // once the migration to WifiTrackerLib is complete.
- @VisibleForTesting internal const val MAX_VALID_LEVEL = 4
}
}
companion object {
- // TODO(b/292534484): Use [com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN] instead
- // once the migration to WifiTrackerLib is complete.
- @VisibleForTesting internal const val MIN_VALID_LEVEL = 0
+ @VisibleForTesting internal const val MIN_VALID_LEVEL = WifiEntry.WIFI_LEVEL_MIN
}
/**
@@ -367,18 +364,17 @@
const val TYPE_ACTIVE = "Active"
const val COL_NETWORK_TYPE = "type"
-const val COL_NETWORK_ID = "networkId"
const val COL_SUB_ID = "subscriptionId"
const val COL_VALIDATED = "isValidated"
const val COL_LEVEL = "level"
const val COL_NUM_LEVELS = "maxLevel"
const val COL_SSID = "ssid"
const val COL_HOTSPOT = "hotspot"
-const val COL_PASSPOINT_ACCESS_POINT = "isPasspointAccessPoint"
-const val COL_ONLINE_SIGN_UP = "isOnlineSignUpForPasspointAccessPoint"
-const val COL_PASSPOINT_NAME = "passpointProviderFriendlyName"
val LEVEL_DEFAULT: String? = null
val NUM_LEVELS_DEFAULT: String? = null
-val NETWORK_ID_DEFAULT: String? = null
val SUB_ID_DEFAULT: String? = null
+
+private const val DO_NOT_USE_COPY_ERROR =
+ "This should only be an issue if the caller incorrectly used `copy` to get a new instance. " +
+ "Please use the `of` method instead."
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index 21ec14f..cf9f9f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -23,6 +23,7 @@
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.AlarmTile
import com.android.systemui.qs.tiles.CameraToggleTile
@@ -157,6 +158,7 @@
labelRes = R.string.quick_settings_flashlight_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject FlashlightTile into tileViewModelMap in QSModule */
@@ -192,7 +194,8 @@
policy =
QSTilePolicy.Restricted(
listOf(DISALLOW_SHARE_LOCATION, DISALLOW_CONFIG_LOCATION)
- )
+ ),
+ category = TileCategory.PRIVACY,
)
/** Inject LocationTile into tileViewModelMap in QSModule */
@@ -225,6 +228,7 @@
labelRes = R.string.status_bar_alarm,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject AlarmTile into tileViewModelMap in QSModule */
@@ -257,6 +261,7 @@
labelRes = R.string.quick_settings_ui_mode_night_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/** Inject uimodenight into tileViewModelMap in QSModule */
@@ -290,6 +295,7 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
autoRemoveOnUnavailable = false,
+ category = TileCategory.PRIVACY,
)
/** Inject work mode into tileViewModelMap in QSModule */
@@ -323,6 +329,7 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
policy = QSTilePolicy.Restricted(listOf(DISALLOW_CAMERA_TOGGLE)),
+ category = TileCategory.PRIVACY,
)
/** Inject camera toggle tile into tileViewModelMap in QSModule */
@@ -365,6 +372,7 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
policy = QSTilePolicy.Restricted(listOf(DISALLOW_MICROPHONE_TOGGLE)),
+ category = TileCategory.PRIVACY,
)
/** Inject microphone toggle tile into tileViewModelMap in QSModule */
@@ -403,10 +411,11 @@
tileSpec = TileSpec.create(DND_TILE_SPEC),
uiConfig =
QSTileUIConfig.Resource(
- iconRes = R.drawable.qs_dnd_icon_off,
+ iconRes = com.android.internal.R.drawable.ic_zen_priority_modes,
labelRes = R.string.quick_settings_modes_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
} else {
QSTileConfig(
@@ -417,6 +426,7 @@
labelRes = R.string.quick_settings_dnd_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 71bcdfcb..b81af86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -22,8 +22,12 @@
import com.android.internal.R;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.notification.modes.ZenIconLoader;
+import com.android.systemui.common.ui.GlobalConfig;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogBufferFactory;
import com.android.systemui.settings.UserTracker;
@@ -79,6 +83,7 @@
import dagger.Provides;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import javax.inject.Named;
@@ -100,9 +105,12 @@
@Binds
CastController provideCastController(CastControllerImpl controllerImpl);
- /** */
+ /**
+ * @deprecated: unscoped configuration controller shouldn't be injected as it might lead to
+ * wrong updates in case of secondary displays.
+ */
@Binds
- ConfigurationController bindConfigurationController(ConfigurationControllerImpl impl);
+ ConfigurationController bindConfigurationController(@GlobalConfig ConfigurationController impl);
/** */
@Binds
@@ -178,6 +186,15 @@
DevicePostureControllerImpl devicePostureControllerImpl);
/** */
+ @Provides
+ @SysUISingleton
+ @GlobalConfig
+ static ConfigurationController provideGlobalConfigurationController(
+ @Application Context context, ConfigurationControllerImpl.Factory factory) {
+ return factory.create(context);
+ }
+
+ /** */
@SysUISingleton
@Provides
static AccessPointControllerImpl provideAccessPointControllerImpl(
@@ -236,4 +253,12 @@
static LogBuffer provideCastControllerLog(LogBufferFactory factory) {
return factory.create("CastControllerLog", 50);
}
+
+ /** Provides a {@link ZenIconLoader} that fetches icons in a background thread. */
+ @Provides
+ @SysUISingleton
+ static ZenIconLoader provideZenIconLoader(
+ @UiBackground ExecutorService backgroundExecutorService) {
+ return new ZenIconLoader(backgroundExecutorService);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index a67b47a..dbeaa59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -23,16 +23,20 @@
import android.util.Log
import androidx.concurrent.futures.await
import com.android.settingslib.notification.data.repository.ZenModeRepository
+import com.android.settingslib.notification.modes.ZenIcon
import com.android.settingslib.notification.modes.ZenIconLoader
import com.android.settingslib.notification.modes.ZenMode
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
+import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
+import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo
import java.time.Duration
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
/**
@@ -45,9 +49,9 @@
private val context: Context,
private val zenModeRepository: ZenModeRepository,
private val notificationSettingsRepository: NotificationSettingsRepository,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ private val iconLoader: ZenIconLoader,
) {
- private val iconLoader: ZenIconLoader = ZenIconLoader.getInstance()
-
val isZenModeEnabled: Flow<Boolean> =
zenModeRepository.globalZenMode
.map {
@@ -76,34 +80,29 @@
val modes: Flow<List<ZenMode>> = zenModeRepository.modes
- val activeModes: Flow<List<ZenMode>> =
- modes.map { modes -> modes.filter { mode -> mode.isActive } }.distinctUntilChanged()
+ /** Flow returning the currently active mode(s), if any. */
+ val activeModes: Flow<ActiveZenModes> =
+ modes
+ .map { modes -> buildActiveZenModes(modes) }
+ .flowOn(bgDispatcher)
+ .distinctUntilChanged()
- /** Flow returning the most prioritized of the active modes, if any. */
- val mainActiveMode: Flow<ZenMode?> =
- activeModes.map { modes -> getMainActiveMode(modes) }.distinctUntilChanged()
+ suspend fun getActiveModes() = buildActiveZenModes(zenModeRepository.getModes())
- /**
- * Given the list of modes (which may include zero or more currently active modes), returns the
- * most prioritized of the active modes, if any.
- */
- private fun getMainActiveMode(modes: List<ZenMode>): ZenMode? {
- return modes.sortedWith(ZenMode.PRIORITIZING_COMPARATOR).firstOrNull { it.isActive }
+ private suspend fun buildActiveZenModes(modes: List<ZenMode>): ActiveZenModes {
+ val activeModesList =
+ modes.filter { mode -> mode.isActive }.sortedWith(ZenMode.PRIORITIZING_COMPARATOR)
+ val mainActiveMode =
+ activeModesList.firstOrNull()?.let { ZenModeInfo(it.name, getModeIcon(it)) }
+
+ return ActiveZenModes(activeModesList.map { m -> m.name }, mainActiveMode)
}
- suspend fun getModeIcon(mode: ZenMode): Icon {
- return iconLoader.getIcon(context, mode).await().drawable().asIcon()
- }
+ val mainActiveMode: Flow<ZenModeInfo?> =
+ activeModes.map { a -> a.mainMode }.distinctUntilChanged()
- /**
- * Given the list of modes (which may include zero or more currently active modes), returns an
- * icon representing the active mode, if any (or, if multiple modes are active, to the most
- * prioritized one). This icon is suitable for use in the status bar or lockscreen (uses the
- * standard DND icon for implicit modes, instead of the launcher icon of the associated
- * package).
- */
- suspend fun getActiveModeIcon(modes: List<ZenMode>): Icon? {
- return getMainActiveMode(modes)?.let { m -> getModeIcon(m) }
+ suspend fun getModeIcon(mode: ZenMode): ZenIcon {
+ return iconLoader.getIcon(context, mode).await()
}
fun activateMode(zenMode: ZenMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt
new file mode 100644
index 0000000..569e517
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.statusbar.policy.domain.model
+
+import com.android.settingslib.notification.modes.ZenMode
+
+/**
+ * Represents the list of [ZenMode] instances that are currently active.
+ *
+ * @property modeNames Names of all the active modes, sorted by their priority.
+ * @property mainMode The most prioritized active mode, if any modes active. Guaranteed to be
+ * non-null if [modeNames] is not empty.
+ */
+data class ActiveZenModes(val modeNames: List<String>, val mainMode: ZenModeInfo?) {
+ fun isAnyActive(): Boolean = modeNames.isNotEmpty()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt
index 60d97d1..5004f4c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.bouncer.shared.flag
+package com.android.systemui.statusbar.policy.domain.model
-import com.android.systemui.kosmos.Kosmos
+import com.android.settingslib.notification.modes.ZenIcon
+import com.android.settingslib.notification.modes.ZenMode
-var Kosmos.fakeComposeBouncerFlags by Kosmos.Fixture { FakeComposeBouncerFlags() }
-val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
+/** Name and icon of a [ZenMode] */
+data class ZenModeInfo(val name: String, val icon: ZenIcon)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
index 3fffd9f..af93880 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy.ui.dialog.composable
+import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@@ -30,9 +31,15 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.android.systemui.common.ui.compose.Icon
@@ -40,12 +47,16 @@
@Composable
fun ModeTile(viewModel: ModeTileViewModel) {
- val tileColor =
- if (viewModel.enabled) MaterialTheme.colorScheme.primary
- else MaterialTheme.colorScheme.surfaceVariant
- val contentColor =
- if (viewModel.enabled) MaterialTheme.colorScheme.onPrimary
- else MaterialTheme.colorScheme.onSurfaceVariant
+ val tileColor: Color by
+ animateColorAsState(
+ if (viewModel.enabled) MaterialTheme.colorScheme.primary
+ else MaterialTheme.colorScheme.surfaceVariant
+ )
+ val contentColor: Color by
+ animateColorAsState(
+ if (viewModel.enabled) MaterialTheme.colorScheme.onPrimary
+ else MaterialTheme.colorScheme.onSurfaceVariant
+ )
CompositionLocalProvider(LocalContentColor provides contentColor) {
Surface(
@@ -56,9 +67,11 @@
modifier =
Modifier.combinedClickable(
onClick = viewModel.onClick,
- onLongClick = viewModel.onLongClick
+ onLongClick = viewModel.onLongClick,
+ onLongClickLabel = viewModel.onLongClickLabel
)
- .padding(20.dp),
+ .padding(20.dp)
+ .semantics { stateDescription = viewModel.stateDescription },
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement =
Arrangement.spacedBy(
@@ -76,7 +89,12 @@
Text(
viewModel.subtext,
fontWeight = FontWeight.W400,
- modifier = Modifier.tileMarquee().testTag("state")
+ modifier =
+ Modifier.tileMarquee()
+ .testTag(if (viewModel.enabled) "stateOn" else "stateOff")
+ .clearAndSetSemantics {
+ contentDescription = viewModel.subtextDescription
+ }
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
index 7c1cb6a..abd2453 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
@@ -28,7 +28,10 @@
val icon: Icon,
val text: String,
val subtext: String,
+ val subtextDescription: String, // version of subtext without "on"/"off" for screen readers
val enabled: Boolean,
+ val stateDescription: String, // "on"/"off" state of the tile, for screen readers
val onClick: () -> Unit,
val onLongClick: () -> Unit,
+ val onLongClickLabel: String, // for screen readers
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
index be90bec..6764839c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
@@ -23,6 +23,7 @@
import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID
import com.android.settingslib.notification.modes.EnableZenModeDialog
import com.android.settingslib.notification.modes.ZenMode
+import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger
@@ -88,10 +89,15 @@
modesList.map { mode ->
ModeTileViewModel(
id = mode.id,
- icon = zenModeInteractor.getModeIcon(mode),
+ icon = zenModeInteractor.getModeIcon(mode).drawable().asIcon(),
text = mode.name,
subtext = getTileSubtext(mode),
+ subtextDescription = getModeDescription(mode) ?: "",
enabled = mode.isActive,
+ stateDescription =
+ context.getString(
+ if (mode.isActive) R.string.zen_mode_on else R.string.zen_mode_off
+ ),
onClick = {
if (!mode.rule.isEnabled) {
openSettings(mode)
@@ -112,7 +118,9 @@
}
}
},
- onLongClick = { openSettings(mode) }
+ onLongClick = { openSettings(mode) },
+ onLongClickLabel =
+ context.resources.getString(R.string.accessibility_long_click_tile)
)
}
}
@@ -127,23 +135,36 @@
dialogDelegate.launchFromDialog(intent)
}
- private fun getTileSubtext(mode: ZenMode): String {
+ /**
+ * Returns a description of the mode, which is:
+ * * a prompt to set up the mode if it is not enabled
+ * * if it cannot be manually activated, text that says so
+ * * otherwise, the trigger description of the mode if it exists...
+ * * ...or null if it doesn't
+ *
+ * This description is used directly for the content description of a mode tile for screen
+ * readers, and for the tile subtext will be augmented with the current status of the mode.
+ */
+ private fun getModeDescription(mode: ZenMode): String? {
if (!mode.rule.isEnabled) {
return context.resources.getString(R.string.zen_mode_set_up)
}
if (!mode.rule.isManualInvocationAllowed && !mode.isActive) {
return context.resources.getString(R.string.zen_mode_no_manual_invocation)
}
+ return mode.getDynamicDescription(context)
+ }
- val modeSubtext = mode.getDynamicDescription(context)
+ private fun getTileSubtext(mode: ZenMode): String {
+ val modeDescription = getModeDescription(mode)
return if (mode.isActive) {
- if (modeSubtext != null) {
- context.getString(R.string.zen_mode_on_with_details, modeSubtext)
+ if (modeDescription != null) {
+ context.getString(R.string.zen_mode_on_with_details, modeDescription)
} else {
context.getString(R.string.zen_mode_on)
}
} else {
- modeSubtext ?: context.getString(R.string.zen_mode_off)
+ modeDescription ?: context.getString(R.string.zen_mode_off)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt b/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
index 4b0e5d1..6d99183 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
@@ -29,11 +29,12 @@
class TelephonyInteractor
@Inject
constructor(
- repository: TelephonyRepository,
+ private val repository: TelephonyRepository,
) {
@Annotation.CallState val callState: Flow<Int> = repository.callState
val isInCall: StateFlow<Boolean> = repository.isInCall
- val hasTelephonyRadio: Boolean = repository.hasTelephonyRadio
+ val hasTelephonyRadio: Boolean
+ get() = repository.hasTelephonyRadio
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
index e3666ce..084da2c 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
@@ -23,7 +23,7 @@
override val gestureDistanceThresholdPx: Int,
override val gestureStateChangedCallback: (GestureState) -> Unit
) :
- TouchpadGestureMonitor by ThreeFingerGestureMonitor(
+ TouchpadGestureMonitor by ThreeFingerDistanceBasedGestureMonitor(
gestureDistanceThresholdPx = gestureDistanceThresholdPx,
gestureStateChangedCallback = gestureStateChangedCallback,
donePredicate =
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
index a410f99..a9aa5c8 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
@@ -21,7 +21,7 @@
override val gestureDistanceThresholdPx: Int,
override val gestureStateChangedCallback: (GestureState) -> Unit
) :
- TouchpadGestureMonitor by ThreeFingerGestureMonitor(
+ TouchpadGestureMonitor by ThreeFingerDistanceBasedGestureMonitor(
gestureDistanceThresholdPx = gestureDistanceThresholdPx,
gestureStateChangedCallback = gestureStateChangedCallback,
donePredicate =
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt
new file mode 100644
index 0000000..5828239
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import androidx.compose.ui.input.pointer.util.VelocityTracker1D
+import kotlin.math.abs
+
+/**
+ * Monitors recent apps gesture completion. That is - using three fingers on touchpad - swipe up
+ * over some distance threshold and then slow down gesture before fingers are lifted. Implementation
+ * is based on [com.android.quickstep.util.TriggerSwipeUpTouchTracker]
+ */
+class RecentAppsGestureMonitor(
+ override val gestureDistanceThresholdPx: Int,
+ override val gestureStateChangedCallback: (GestureState) -> Unit,
+ private val velocityThresholdPxPerMs: Float,
+ private val velocityTracker: VelocityTracker1D = VelocityTracker1D(isDataDifferential = false),
+) : TouchpadGestureMonitor {
+
+ private var xStart = 0f
+ private var yStart = 0f
+
+ override fun processTouchpadEvent(event: MotionEvent) {
+ val action = event.actionMasked
+ velocityTracker.addDataPoint(event.eventTime, event.y)
+ when (action) {
+ MotionEvent.ACTION_DOWN -> {
+ if (isThreeFingerTouchpadSwipe(event)) {
+ xStart = event.x
+ yStart = event.y
+ gestureStateChangedCallback(GestureState.IN_PROGRESS)
+ }
+ }
+ MotionEvent.ACTION_UP -> {
+ if (isThreeFingerTouchpadSwipe(event) && isRecentAppsGesture(event)) {
+ gestureStateChangedCallback(GestureState.FINISHED)
+ } else {
+ gestureStateChangedCallback(GestureState.NOT_STARTED)
+ }
+ velocityTracker.resetTracking()
+ }
+ MotionEvent.ACTION_CANCEL -> {
+ velocityTracker.resetTracking()
+ }
+ }
+ }
+
+ private fun isRecentAppsGesture(event: MotionEvent): Boolean {
+ // below is trying to mirror behavior of TriggerSwipeUpTouchTracker#onGestureEnd.
+ // We're diving velocity by 1000, to have the same unit of measure: pixels/ms.
+ val swipeDistance = yStart - event.y
+ val velocity = velocityTracker.calculateVelocity() / 1000
+ return swipeDistance >= gestureDistanceThresholdPx &&
+ abs(velocity) <= velocityThresholdPxPerMs
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerDistanceBasedGestureMonitor.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureMonitor.kt
rename to packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerDistanceBasedGestureMonitor.kt
index 377977c..9bf0fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerDistanceBasedGestureMonitor.kt
@@ -25,8 +25,12 @@
fun wasGestureDone(startX: Float, startY: Float, endX: Float, endY: Float): Boolean
}
-/** Common implementation for all three-finger gesture monitors */
-class ThreeFingerGestureMonitor(
+/**
+ * Common implementation for three-finger gesture monitors that are only distance-based. E.g. recent
+ * apps gesture is not only distance-based because it requires going over threshold distance and
+ * slowing down the movement.
+ */
+class ThreeFingerDistanceBasedGestureMonitor(
override val gestureDistanceThresholdPx: Int,
override val gestureStateChangedCallback: (GestureState) -> Unit,
private val donePredicate: GestureDonePredicate
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
index ecf1165..70774f13 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -28,6 +28,7 @@
import dagger.Provides;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.inject.Singleton;
@@ -81,6 +82,18 @@
@Singleton
@UiBackground
public static Executor provideUiBackgroundExecutor() {
+ return provideUiBackgroundExecutorService();
+ }
+
+ /**
+ * Provide an ExecutorService specifically for running UI operations on a separate thread.
+ *
+ * <p>Keep submitted runnables short and to the point, just as with any other UI code.
+ */
+ @Provides
+ @Singleton
+ @UiBackground
+ public static ExecutorService provideUiBackgroundExecutorService() {
return Executors.newSingleThreadExecutor();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt
index e6e2a07..ee00e8b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt
@@ -35,17 +35,3 @@
}
.onStart { emit(isReduceBrightColorsActivated) }
}
-
-fun ReduceBrightColorsController.isAvailable(): Flow<Boolean> {
- return conflatedCallbackFlow {
- val callback =
- object : ReduceBrightColorsController.Listener {
- override fun onFeatureEnabledChanged(enabled: Boolean) {
- trySend(enabled)
- }
- }
- addCallback(callback)
- awaitClose { removeCallback(callback) }
- }
- .onStart { emit(isReduceBrightColorsFeatureAvailable) }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
index 1112d6f..a5c8af5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
@@ -26,7 +26,7 @@
/**
* A state comprised of a [value] of type [T] paired with a boolean indicating whether or not the
- * [value] [isAnimating] in the UI.
+ * value [isAnimating][isAnimating] in the UI.
*/
sealed interface AnimatedValue<out T> {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 28effe9..030a20a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -391,11 +391,7 @@
}
public void notifyVisible(boolean visible) {
- if (Flags.useVolumeController()) {
- mVolumeControllerAdapter.notifyVolumeControllerVisible(visible);
- } else {
- mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
- }
+ mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
}
public void userActivity() {
@@ -457,7 +453,11 @@
}
private void onNotifyVisibleW(boolean visible) {
- mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
+ if (Flags.useVolumeController()) {
+ mVolumeControllerAdapter.notifyVolumeControllerVisible(visible);
+ } else {
+ mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
+ }
if (!visible) {
if (updateActiveStreamW(-1)) {
mCallbacks.onStateChanged(mState);
@@ -1280,6 +1280,8 @@
private final class Receiver extends BroadcastReceiver {
+ private static final int STREAM_UNKNOWN = -1;
+
public void init() {
final IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
@@ -1301,30 +1303,38 @@
final String action = intent.getAction();
boolean changed = false;
if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
- final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
- final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
+ final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+ STREAM_UNKNOWN);
final int oldLevel = intent
.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
- + " level=" + level + " oldLevel=" + oldLevel);
- changed = updateStreamLevelW(stream, level);
+ + " oldLevel=" + oldLevel);
+ if (stream != STREAM_UNKNOWN) {
+ changed |= onVolumeChangedW(stream, 0);
+ }
} else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
- final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+ STREAM_UNKNOWN);
final int devices = intent
.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
final int oldDevices = intent
.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
+ stream + " devices=" + devices + " oldDevices=" + oldDevices);
- changed = checkRoutedToBluetoothW(stream);
- changed |= onVolumeChangedW(stream, 0);
+ if (stream != STREAM_UNKNOWN) {
+ changed |= checkRoutedToBluetoothW(stream);
+ changed |= onVolumeChangedW(stream, 0);
+ }
} else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
- final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+ STREAM_UNKNOWN);
final boolean muted = intent
.getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
+ " muted=" + muted);
- changed = updateStreamMuteW(stream, muted);
+ if (stream != STREAM_UNKNOWN) {
+ changed = updateStreamMuteW(stream, muted);
+ }
} else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
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..28a43df 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
@@ -16,35 +16,16 @@
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.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
+import dagger.Binds
import dagger.Module
-import dagger.Provides
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
@Module
interface CaptioningModule {
- companion object {
-
- @Provides
- @SysUISingleton
- fun provideCaptioningRepository(
- captioningManager: CaptioningManager,
- @Background coroutineContext: CoroutineContext,
- @Application coroutineScope: CoroutineScope,
- ): CaptioningRepository =
- CaptioningRepositoryImpl(captioningManager, coroutineContext, coroutineScope)
-
- @Provides
- @SysUISingleton
- fun provideCaptioningInteractor(repository: CaptioningRepository): CaptioningInteractor =
- CaptioningInteractor(repository)
- }
+ @Binds
+ @SysUISingleton
+ fun bindCaptioningRepository(impl: CaptioningRepositoryImpl): CaptioningRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index 4f77cd0..73728e6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
@@ -75,7 +75,7 @@
}
.map { it ?: AudioOutputDevice.Unknown }
.flowOn(backgroundCoroutineContext)
- .stateIn(scope, SharingStarted.Eagerly, AudioOutputDevice.Unknown)
+ .stateIn(scope, SharingStarted.Eagerly, AudioOutputDevice.Unavailable)
private fun AudioDeviceInfo.toAudioOutputDevice(): AudioOutputDevice {
if (
@@ -120,6 +120,11 @@
name = name,
icon = icon,
)
+ deviceType == MediaDeviceType.TYPE_CAST_DEVICE ->
+ AudioOutputDevice.Remote(
+ name = name,
+ icon = icon,
+ )
else ->
AudioOutputDevice.BuiltIn(
name = name,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/model/AudioOutputDevice.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/model/AudioOutputDevice.kt
index ba0b082..0e4cac0b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/model/AudioOutputDevice.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/model/AudioOutputDevice.kt
@@ -31,6 +31,12 @@
override val icon: Drawable?,
) : AudioOutputDevice
+ /** Models a cast audio output device. */
+ data class Remote(
+ override val name: String,
+ override val icon: Drawable?,
+ ) : AudioOutputDevice
+
/** Models a wired audio output device. */
data class Wired(
override val name: String,
@@ -52,4 +58,16 @@
override val icon: Drawable
get() = error("Unsupported for unknown device")
}
+
+ /**
+ * Models a state when current audio output device is not loaded yet or the system failed to
+ * load it.
+ */
+ data object Unavailable : AudioOutputDevice {
+ override val name: String
+ get() = error("Unsupported for unavailable device")
+
+ override val icon: Drawable
+ get() = error("Unsupported for unavailable device")
+ }
}
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..2e5e389 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
@@ -26,7 +26,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
@VolumePanelScope
class CaptioningAvailabilityCriteria
@@ -45,7 +45,7 @@
else VolumePanelUiEvent.VOLUME_PANEL_LIVE_CAPTION_TOGGLE_GONE
)
}
- .shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
override fun isAvailable(): Flow<Boolean> = availability
}
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/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
index a270d5ff..f94cbda 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
@@ -74,34 +74,51 @@
)
private val currentAudioDevice: Flow<AudioOutputDevice> =
- audioOutputInteractor.currentAudioDevice.filter { it !is AudioOutputDevice.Unknown }
+ audioOutputInteractor.currentAudioDevice.filter { it !is AudioOutputDevice.Unavailable }
+ /**
+ * Model for the Media Output component in the Volume Panel. It's guaranteed to have an
+ * available device if it's loaded.
+ */
val mediaOutputModel: StateFlow<Result<MediaOutputComponentModel>> =
- audioModeInteractor.isOngoingCall
- .flatMapLatest { isOngoingCall ->
- audioSharingInteractor.isInAudioSharing.flatMapLatest { isInAudioSharing ->
- if (isOngoingCall) {
- currentAudioDevice.map {
- MediaOutputComponentModel.Calling(it, isInAudioSharing)
- }
- } else {
- combine(sessionWithPlaybackState.filterData(), currentAudioDevice) {
- sessionWithPlaybackState,
- currentAudioDevice ->
- if (sessionWithPlaybackState == null) {
- MediaOutputComponentModel.Idle(currentAudioDevice, isInAudioSharing)
- } else {
- MediaOutputComponentModel.MediaSession(
- sessionWithPlaybackState.session,
- sessionWithPlaybackState.isPlaybackActive,
- currentAudioDevice,
- isInAudioSharing,
- )
- }
+ combine(
+ audioSharingInteractor.isInAudioSharing,
+ audioModeInteractor.isOngoingCall,
+ currentAudioDevice,
+ ) { isInAudioSharing, isOngoingCall, currentAudioDevice ->
+ if (isOngoingCall) {
+ flowOf(
+ MediaOutputComponentModel.Calling(
+ device = currentAudioDevice,
+ isInAudioSharing = isInAudioSharing,
+ canOpenAudioSwitcher = false,
+ )
+ )
+ } else {
+ sessionWithPlaybackState.filterData().map { sessionWithPlaybackState ->
+ if (sessionWithPlaybackState == null) {
+ MediaOutputComponentModel.Idle(
+ device = currentAudioDevice,
+ isInAudioSharing = isInAudioSharing,
+ canOpenAudioSwitcher =
+ !isInAudioSharing &&
+ currentAudioDevice !is AudioOutputDevice.Unknown,
+ )
+ } else {
+ MediaOutputComponentModel.MediaSession(
+ session = sessionWithPlaybackState.session,
+ isPlaybackActive = sessionWithPlaybackState.isPlaybackActive,
+ device = currentAudioDevice,
+ isInAudioSharing = isInAudioSharing,
+ canOpenAudioSwitcher =
+ !isInAudioSharing &&
+ currentAudioDevice !is AudioOutputDevice.Unknown,
+ )
}
}
}
}
+ .flatMapLatest { it }
.wrapInResult()
.stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index 31a8977..aa07cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -179,9 +179,7 @@
return MediaDeviceSession(
packageName = packageName,
sessionToken = sessionToken,
- canAdjustVolume =
- playbackInfo != null &&
- playbackInfo?.volumeControl != VolumeProvider.VOLUME_CONTROL_FIXED,
+ canAdjustVolume = playbackInfo.volumeControl != VolumeProvider.VOLUME_CONTROL_FIXED,
appLabel = getApplicationLabel(packageName) ?: return null
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
index 220fb2b..6588b44 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
@@ -24,11 +24,13 @@
val device: AudioOutputDevice
val isInAudioSharing: Boolean
+ val canOpenAudioSwitcher: Boolean
/** There is an ongoing call on the device. */
data class Calling(
override val device: AudioOutputDevice,
override val isInAudioSharing: Boolean,
+ override val canOpenAudioSwitcher: Boolean,
) : MediaOutputComponentModel
/** There is media playing on the device. */
@@ -37,11 +39,13 @@
val isPlaybackActive: Boolean,
override val device: AudioOutputDevice,
override val isInAudioSharing: Boolean,
+ override val canOpenAudioSwitcher: Boolean,
) : MediaOutputComponentModel
/** There is nothing playing on the device. */
data class Idle(
override val device: AudioOutputDevice,
override val isInAudioSharing: Boolean,
+ override val canOpenAudioSwitcher: Boolean,
) : MediaOutputComponentModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt
index 8ba672d..42f88b4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt
@@ -16,11 +16,15 @@
package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel
+import com.android.systemui.common.shared.model.Color
+
/**
* Models part of the Media Session Volume Panel component that displays connected device
* information.
*/
data class ConnectedDeviceViewModel(
val label: CharSequence,
+ val labelColor: Color,
val deviceName: CharSequence?,
+ val deviceNameColor: Color,
)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index 36b42f2..e565de5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -75,12 +75,25 @@
}
}
ConnectedDeviceViewModel(
- label,
- if (mediaOutputModel.isInAudioSharing) {
- context.getString(R.string.audio_sharing_description)
- } else {
- mediaOutputModel.device.name
- },
+ label = label,
+ labelColor =
+ Color.Attribute(com.android.internal.R.attr.materialColorOnSurfaceVariant),
+ deviceName =
+ if (mediaOutputModel.isInAudioSharing) {
+ context.getString(R.string.audio_sharing_description)
+ } else {
+ mediaOutputModel.device
+ .takeIf { it !is AudioOutputDevice.Unknown }
+ ?.name ?: context.getString(R.string.media_seamless_other_device)
+ },
+ deviceNameColor =
+ if (mediaOutputModel.canOpenAudioSwitcher) {
+ Color.Attribute(com.android.internal.R.attr.materialColorOnSurface)
+ } else {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorOnSurfaceVariant
+ )
+ },
)
}
.stateIn(
@@ -107,19 +120,39 @@
DeviceIconViewModel.IsPlaying(
icon = icon,
iconColor =
- Color.Attribute(com.android.internal.R.attr.materialColorSurface),
+ if (mediaOutputModel.canOpenAudioSwitcher) {
+ Color.Attribute(com.android.internal.R.attr.materialColorSurface)
+ } else {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorSurfaceContainerHighest
+ )
+ },
backgroundColor =
- Color.Attribute(com.android.internal.R.attr.materialColorSecondary),
+ if (mediaOutputModel.canOpenAudioSwitcher) {
+ Color.Attribute(com.android.internal.R.attr.materialColorSecondary)
+ } else {
+ Color.Attribute(com.android.internal.R.attr.materialColorOutline)
+ },
)
} else {
DeviceIconViewModel.IsNotPlaying(
icon = icon,
iconColor =
- Color.Attribute(
- com.android.internal.R.attr.materialColorOnSurfaceVariant
- ),
+ if (mediaOutputModel.canOpenAudioSwitcher) {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorOnSurfaceVariant
+ )
+ } else {
+ Color.Attribute(com.android.internal.R.attr.materialColorOutline)
+ },
backgroundColor =
- Color.Attribute(com.android.internal.R.attr.materialColorSurface),
+ if (mediaOutputModel.canOpenAudioSwitcher) {
+ Color.Attribute(com.android.internal.R.attr.materialColorSurface)
+ } else {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorSurfaceContainerHighest
+ )
+ },
)
}
}
@@ -132,7 +165,7 @@
val enabled: StateFlow<Boolean> =
mediaOutputComponentInteractor.mediaOutputModel
.filterData()
- .map { !it.isInAudioSharing }
+ .map { it.canOpenAudioSwitcher }
.stateIn(
coroutineScope,
SharingStarted.Eagerly,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
index cfcd6b1..56d0bce 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
@@ -63,13 +63,7 @@
private val changes = MutableSharedFlow<Unit>()
private val currentAudioDeviceAttributes: StateFlow<AudioDeviceAttributes?> =
audioOutputInteractor.currentAudioDevice
- .map { audioDevice ->
- if (audioDevice is AudioOutputDevice.Unknown) {
- builtinSpeaker
- } else {
- audioDevice.getAudioDeviceAttributes()
- }
- }
+ .map { audioDevice -> audioDevice.getAudioDeviceAttributes() }
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), builtinSpeaker)
/**
@@ -185,7 +179,10 @@
.firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
}
}
- else -> null
+ is AudioOutputDevice.Wired -> null
+ is AudioOutputDevice.Remote -> null
+ is AudioOutputDevice.Unknown -> builtinSpeaker
+ is AudioOutputDevice.Unavailable -> builtinSpeaker
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index 4841c78..dd1c11d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -23,8 +23,15 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.pipeline.shared.TileSpec;
+import com.android.systemui.qs.shared.model.TileCategory;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.QuickAccessWalletTile;
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
+import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy;
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
+import com.android.systemui.res.R;
import com.android.systemui.wallet.controller.WalletContextualLocationsService;
import com.android.systemui.wallet.ui.WalletActivity;
@@ -43,6 +50,8 @@
@Module
public abstract class WalletModule {
+ public static final String WALLET_TILE_SPEC = "wallet";
+
@Binds
@IntoMap
@ClassKey(WalletContextualLocationsService.class)
@@ -69,4 +78,22 @@
@StringKey(QuickAccessWalletTile.TILE_SPEC)
public abstract QSTileImpl<?> bindQuickAccessWalletTile(
QuickAccessWalletTile quickAccessWalletTile);
+
+ @Provides
+ @IntoMap
+ @StringKey(WALLET_TILE_SPEC)
+ public static QSTileConfig provideQuickAccessWalletTileConfig(QsEventLogger uiEventLogger) {
+ TileSpec tileSpec = TileSpec.create(WALLET_TILE_SPEC);
+ return new QSTileConfig(
+ tileSpec,
+ new QSTileUIConfig.Resource(
+ R.drawable.ic_wallet_lockscreen,
+ R.string.wallet_title
+ ),
+ uiEventLogger.getNewInstanceId(),
+ TileCategory.UTILITIES,
+ tileSpec.getSpec(),
+ QSTilePolicy.NoRestrictions.INSTANCE
+ );
+ }
}
diff --git a/packages/SystemUI/tests/Android.bp b/packages/SystemUI/tests/Android.bp
index 957ed87..3e7596c 100644
--- a/packages/SystemUI/tests/Android.bp
+++ b/packages/SystemUI/tests/Android.bp
@@ -50,3 +50,14 @@
additional_manifests: ["AndroidManifest.xml"],
manifest: "AndroidManifest-base.xml",
}
+
+test_module_config {
+ name: "SystemUITests_systemui_accessibility",
+ base: "SystemUITests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.systemui.accessibility"],
+ exclude_annotations: [
+ "android.platform.test.annotations.Postsubmit",
+ "android.platform.test.annotations.FlakyTest",
+ ],
+}
diff --git a/packages/SystemUI/tests/goldens/bouncerPredictiveBackMotion.json b/packages/SystemUI/tests/goldens/bouncerPredictiveBackMotion.json
new file mode 100644
index 0000000..f37580d
--- /dev/null
+++ b/packages/SystemUI/tests/goldens/bouncerPredictiveBackMotion.json
@@ -0,0 +1,831 @@
+{
+ "frame_ids": [
+ "before",
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944,
+ 960,
+ 976,
+ 992,
+ 1008,
+ 1024,
+ "after"
+ ],
+ "features": [
+ {
+ "name": "content_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9954499,
+ 0.9805035,
+ 0.9527822,
+ 0.9092045,
+ 0.84588075,
+ 0.7583043,
+ 0.6424476,
+ 0.49766344,
+ 0.33080608,
+ 0.15650165,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ {
+ "type": "not_found"
+ }
+ ]
+ },
+ {
+ "name": "content_scale",
+ "type": "scale",
+ "data_points": [
+ "default",
+ {
+ "x": 0.9995097,
+ "y": 0.9995097,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.997352,
+ "y": 0.997352,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.990635,
+ "y": 0.990635,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.97249764,
+ "y": 0.97249764,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.94287145,
+ "y": 0.94287145,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.9128026,
+ "y": 0.9128026,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8859569,
+ "y": 0.8859569,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8629254,
+ "y": 0.8629254,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8442908,
+ "y": 0.8442908,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8303209,
+ "y": 0.8303209,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8205137,
+ "y": 0.8205137,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.81387186,
+ "y": 0.81387186,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.80941653,
+ "y": 0.80941653,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.80641484,
+ "y": 0.80641484,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.80437464,
+ "y": 0.80437464,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.80297637,
+ "y": 0.80297637,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.80201286,
+ "y": 0.80201286,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8013477,
+ "y": 0.8013477,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8008894,
+ "y": 0.8008894,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8005756,
+ "y": 0.8005756,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.80036324,
+ "y": 0.80036324,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8002219,
+ "y": 0.8002219,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.80012995,
+ "y": 0.80012995,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8000721,
+ "y": 0.8000721,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.80003715,
+ "y": 0.80003715,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8000173,
+ "y": 0.8000173,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.800007,
+ "y": 0.800007,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8000022,
+ "y": 0.8000022,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8000004,
+ "y": 0.8000004,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.79999995,
+ "y": 0.79999995,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "x": 0.8,
+ "y": 0.8,
+ "pivot": "unspecified"
+ },
+ {
+ "type": "not_found"
+ }
+ ]
+ },
+ {
+ "name": "content_offset",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "x": 0,
+ "y": 0.5714286
+ },
+ {
+ "x": 0,
+ "y": 2.857143
+ },
+ {
+ "x": 0,
+ "y": 7.142857
+ },
+ {
+ "x": 0,
+ "y": 13.714286
+ },
+ {
+ "x": 0,
+ "y": 23.142857
+ },
+ {
+ "x": 0,
+ "y": 36.285713
+ },
+ {
+ "x": 0,
+ "y": 53.714287
+ },
+ {
+ "x": 0,
+ "y": 75.42857
+ },
+ {
+ "x": 0,
+ "y": 100.28571
+ },
+ {
+ "x": 0,
+ "y": 126.57143
+ },
+ {
+ "x": 0,
+ "y": 151.42857
+ },
+ {
+ "x": 0,
+ "y": 174
+ },
+ {
+ "x": 0,
+ "y": 193.42857
+ },
+ {
+ "x": 0,
+ "y": 210.28572
+ },
+ {
+ "x": 0,
+ "y": 224.85715
+ },
+ {
+ "x": 0,
+ "y": 237.14285
+ },
+ {
+ "x": 0,
+ "y": 247.71428
+ },
+ {
+ "x": 0,
+ "y": 256.85715
+ },
+ {
+ "x": 0,
+ "y": 264.57144
+ },
+ {
+ "x": 0,
+ "y": 271.42856
+ },
+ {
+ "x": 0,
+ "y": 277.14285
+ },
+ {
+ "x": 0,
+ "y": 282
+ },
+ {
+ "x": 0,
+ "y": 286.2857
+ },
+ {
+ "x": 0,
+ "y": 289.7143
+ },
+ {
+ "x": 0,
+ "y": 292.57144
+ },
+ {
+ "x": 0,
+ "y": 294.85715
+ },
+ {
+ "x": 0,
+ "y": 296.85715
+ },
+ {
+ "x": 0,
+ "y": 298.2857
+ },
+ {
+ "x": 0,
+ "y": 299.14285
+ },
+ {
+ "x": 0,
+ "y": 299.7143
+ },
+ {
+ "x": 0,
+ "y": 300
+ },
+ {
+ "x": 0,
+ "y": 0
+ },
+ {
+ "type": "not_found"
+ }
+ ]
+ },
+ {
+ "name": "background_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9900334,
+ 0.8403853,
+ 0.71002257,
+ 0.5979084,
+ 0.50182605,
+ 0.41945767,
+ 0.34874845,
+ 0.28797746,
+ 0.23573697,
+ 0.19087732,
+ 0.1524564,
+ 0.11970067,
+ 0.091962695,
+ 0.068702936,
+ 0.049464583,
+ 0.033859253,
+ 0.021552086,
+ 0.012255073,
+ 0.005717635,
+ 0.0017191172,
+ 6.711483e-05,
+ 0,
+ {
+ "type": "not_found"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index a8ab922..bf13ceb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -445,15 +445,11 @@
assertFalse(mWifiRepository.isWifiConnectedWithValidSsid());
mWifiRepository.setWifiNetwork(
- new WifiNetworkModel.Active(
- /* networkId= */ 0,
+ WifiNetworkModel.Active.Companion.of(
/* isValidated= */ false,
/* level= */ 0,
/* ssid= */ "",
- /* hotspotDeviceType= */ WifiNetworkModel.HotspotDeviceType.NONE,
- /* isPasspointAccessPoint= */ false,
- /* isOnlineSignUpForPasspointAccessPoint= */ false,
- /* passpointProviderFriendlyName= */ null));
+ /* hotspotDeviceType= */ WifiNetworkModel.HotspotDeviceType.NONE));
assertTrue(mWifiRepository.isWifiConnectedWithValidSsid());
mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index 347605d..c42e25b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -19,6 +19,7 @@
import android.app.ActivityTaskManager
import android.content.pm.PackageManager
import android.os.PowerManager
+import android.platform.test.annotations.EnableFlags
import android.telecom.TelecomManager
import android.telephony.TelephonyManager
import android.testing.TestableLooper
@@ -26,14 +27,20 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
+import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.shade.ShadeController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,6 +71,8 @@
val fakeSystemClock = FakeSystemClock()
val mainExecutor = FakeExecutor(fakeSystemClock)
val backgroundExecutor = FakeExecutor(fakeSystemClock)
+ private val kosmos = testKosmos()
+ private val msdlPlayer = kosmos.fakeMSDLPlayer
lateinit var underTest: EmergencyButtonController
@@ -84,6 +93,7 @@
mainExecutor,
backgroundExecutor,
mSelectedUserInteractor,
+ msdlPlayer,
)
context.setMockPackageManager(packageManager)
Mockito.`when`(emergencyButton.context).thenReturn(context)
@@ -113,4 +123,13 @@
/* isSecure= */ eq(true)
)
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun takeEmergencyCallAction_withMSDLFeedback_playsEmergencyButtonTokenAndNullAttributes() {
+ underTest.takeEmergencyCallAction()
+
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.KEYPRESS_RETURN)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index e724c60..c0d8be3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -27,6 +29,7 @@
import static org.mockito.Mockito.when;
import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper.RunWithLooper;
import android.view.KeyEvent;
@@ -43,9 +46,13 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.haptics.msdl.FakeMSDLPlayer;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.data.model.MSDLToken;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -85,7 +92,11 @@
private FakeFeatureFlags mFeatureFlags;
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private UserActivityNotifier mUserActivityNotifier;
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
+ private KosmosJavaAdapter mKosmosJavaAdapter = new KosmosJavaAdapter(this);
+ private final FakeMSDLPlayer mMSDLPlayer = mKosmosJavaAdapter.getMsdlPlayer();
@Before
public void setup() {
@@ -108,7 +119,8 @@
return new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
- mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor) {
+ mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor, mMSDLPlayer,
+ mUserActivityNotifier) {
@Override
void resetState() {
}
@@ -197,4 +209,32 @@
verify(mAbsKeyInputView, never()).setPasswordEntryInputEnabled(true);
verify(mAbsKeyInputView, never()).setPasswordEntryEnabled(true);
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ public void onPasswordChecked_withMSDLFeedback_withMatch_playsUnlockToken() {
+ mKeyguardAbsKeyInputViewController.onPasswordChecked(0, true, 100, true);
+ assertThat(mMSDLPlayer.getLatestTokenPlayed()).isEqualTo(MSDLToken.UNLOCK);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ public void onPasswordChecked_withoutMSDLFeedback_withMatch_doesNotPlayToken() {
+ mKeyguardAbsKeyInputViewController.onPasswordChecked(0, true, 100, true);
+ assertThat(mMSDLPlayer.getLatestTokenPlayed()).isNull();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ public void onPasswordChecked_withMSDLFeedback_withoutMatch_playsFailureToken() {
+ mKeyguardAbsKeyInputViewController.onPasswordChecked(0, false, 100, true);
+ assertThat(mMSDLPlayer.getLatestTokenPlayed()).isEqualTo(MSDLToken.FAILURE);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ public void onPasswordChecked_withoutMSDLFeedback_withoutMatch_doesNotPlayToken() {
+ mKeyguardAbsKeyInputViewController.onPasswordChecked(0, false, 100, true);
+ assertThat(mMSDLPlayer.getLatestTokenPlayed()).isNull();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 36d4d12..873bc2c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -103,6 +103,7 @@
@Mock lateinit var deleteButton: NumPadButton
@Mock lateinit var enterButton: View
@Mock lateinit var uiEventLogger: UiEventLogger
+ @Mock lateinit var mUserActivityNotifier: UserActivityNotifier
@Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
@@ -149,7 +150,9 @@
featureFlags,
mSelectedUserInteractor,
uiEventLogger,
- keyguardKeyboardInteractor
+ keyguardKeyboardInteractor,
+ null,
+ mUserActivityNotifier
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 7151c42..f141a49 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -69,6 +69,7 @@
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+ @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
private val updateMonitorCallbackArgumentCaptor =
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
@@ -101,7 +102,9 @@
emergencyButtonController,
fakeFeatureFlags,
mSelectedUserInteractor,
- keyguardKeyboardInteractor
+ keyguardKeyboardInteractor,
+ null,
+ mUserActivityNotifier
)
underTest.init()
underTest.onViewAttached()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index acae913..a03c839 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -63,6 +63,7 @@
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+ @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
@Before
fun setup() {
@@ -96,7 +97,9 @@
emergencyButtonController,
fakeFeatureFlags,
mSelectedUserInteractor,
- keyguardKeyboardInteractor
+ keyguardKeyboardInteractor,
+ null,
+ mUserActivityNotifier
)
underTest.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java
index 4a5c1be..038ec40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java
@@ -32,12 +32,14 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -66,7 +68,7 @@
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, MY_USER_ID);
mAccessibilityButtonModeObserver = new AccessibilityButtonModeObserver(mContext,
- mUserTracker);
+ mUserTracker, Mockito.mock(SecureSettings.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java
index a5a7a4a..f564926 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java
@@ -31,12 +31,14 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -62,7 +64,7 @@
public void setUp() {
when(mUserTracker.getUserId()).thenReturn(MY_USER_ID);
mAccessibilityButtonTargetsObserver = new AccessibilityButtonTargetsObserver(mContext,
- mUserTracker);
+ mUserTracker, Mockito.mock(SecureSettings.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java
index ba990ef..afed12f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java
@@ -31,12 +31,14 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -62,7 +64,7 @@
public void setUp() {
when(mUserTracker.getUserId()).thenReturn(MY_USER_ID);
mAccessibilityGestureTargetsObserver = new AccessibilityGestureTargetsObserver(mContext,
- mUserTracker);
+ mUserTracker, Mockito.mock(SecureSettings.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
index 9222fc2..1d88b90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
@@ -27,6 +27,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -72,7 +73,7 @@
protected FakeSecureSettingsContentObserver(Context context, UserTracker userTracker,
String secureSettingsKey) {
- super(context, userTracker, secureSettingsKey);
+ super(context, userTracker, Mockito.mock(SecureSettings.class), secureSettingsKey);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 113a8c0..5e37d4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.display.DisplayManager;
+import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.TestableLooper;
@@ -80,6 +81,7 @@
private AccessibilityManager mAccessibilityManager;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private AccessibilityFloatingMenuController mController;
+ private TestableLooper mTestableLooper;
@Mock
private AccessibilityButtonTargetsObserver mTargetsObserver;
@Mock
@@ -108,6 +110,7 @@
mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
mLazyViewCapture, /* isViewCaptureEnabled= */ false);
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
+ mTestableLooper = TestableLooper.get(this);
when(mTargetsObserver.getCurrentAccessibilityButtonTargets())
.thenReturn(Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(),
@@ -231,7 +234,8 @@
mKeyguardCallback.onKeyguardVisibilityChanged(false);
mKeyguardCallback.onUserSwitching(fakeUserId);
- mKeyguardCallback.onUserSwitchComplete(fakeUserId);
+ mController.mUserInitializationCompleteCallback.onUserInitializationComplete(1);
+ mTestableLooper.processAllMessages();
assertThat(mController.mFloatingMenu).isNotNull();
}
@@ -346,7 +350,8 @@
new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
viewCaptureAwareWindowManager, displayManager, mAccessibilityManager,
mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor, mSecureSettings,
- displayTracker, mNavigationModeController);
+ displayTracker, mNavigationModeController, new Handler(
+ mTestableLooper.getLooper()));
controller.init();
return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index d3b7d22..662815e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -52,7 +52,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -92,6 +91,7 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ private static final int TEST_LAUNCH_SOURCE_ID = 1;
private static final String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF";
private static final String DEVICE_NAME = "test_name";
private static final String TEST_PKG = "pkg";
@@ -124,7 +124,7 @@
@Mock
private AudioManager mAudioManager;
@Mock
- private UiEventLogger mUiEventLogger;
+ private HearingDevicesUiEventLogger mUiEventLogger;
@Mock
private CachedBluetoothDevice mCachedDevice;
@Mock
@@ -182,7 +182,8 @@
anyInt(), any());
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS);
- verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR);
+ verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR,
+ TEST_LAUNCH_SOURCE_ID);
}
@Test
@@ -196,7 +197,8 @@
anyInt(), any());
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
HearingDevicesDialogDelegate.ACTION_BLUETOOTH_DEVICE_DETAILS);
- verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK);
+ verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK,
+ TEST_LAUNCH_SOURCE_ID);
}
@Test
@@ -207,7 +209,8 @@
mDialogDelegate.onDeviceItemClicked(mHearingDeviceItem, new View(mContext));
verify(mCachedDevice).disconnect();
- verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT);
+ verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT,
+ TEST_LAUNCH_SOURCE_ID);
}
@Test
@@ -304,6 +307,7 @@
mDialogDelegate = new HearingDevicesDialogDelegate(
mContext,
true,
+ TEST_LAUNCH_SOURCE_ID,
mDialogFactory,
mActivityStarter,
mDialogTransitionAnimator,
@@ -327,6 +331,7 @@
mDialogDelegate = new HearingDevicesDialogDelegate(
mContext,
false,
+ TEST_LAUNCH_SOURCE_ID,
mDialogFactory,
mActivityStarter,
mDialogTransitionAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index a18d272..aa8c6b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -57,6 +57,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.ambient.touch.dagger.InputSessionComponent;
import com.android.systemui.kosmos.KosmosJavaAdapter;
+import com.android.systemui.log.LogBufferHelperKt;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.display.DisplayHelper;
@@ -153,7 +154,8 @@
when(mWindowManager.getMaximumWindowMetrics()).thenReturn(mWindowMetrics);
mMonitor = new TouchMonitor(mExecutor, mBackgroundExecutor, mLifecycleRegistry,
mInputFactory, mDisplayHelper, mKosmos.getConfigurationInteractor(),
- handlers, mIWindowManager, 0);
+ handlers, mIWindowManager, 0, "TouchMonitorTest",
+ LogBufferHelperKt.logcatLogBuffer("TouchMonitorTest"));
clearInvocations(mLifecycleRegistry);
mMonitor.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 1e23690..7889b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -61,10 +61,12 @@
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.events.ANIMATING_OUT
+import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -145,6 +147,9 @@
private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
+ private val kosmos = testKosmos()
+ private val msdlPlayer = kosmos.msdlPlayer
+
private var authContainer: TestAuthContainerView? = null
@Before
@@ -668,7 +673,8 @@
{ credentialViewModel },
fakeExecutor,
vibrator,
- lazyViewCapture
+ lazyViewCapture,
+ msdlPlayer,
) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 6047e7d..4850510 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -37,12 +37,15 @@
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.Surface
import androidx.test.filters.SmallTest
import com.android.app.activityTaskManager
+import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.Utils.toBitmap
@@ -51,6 +54,7 @@
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
+import com.android.systemui.biometrics.domain.interactor.sideFpsOverlayInteractor
import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
import com.android.systemui.biometrics.extractAuthenticatorTypes
import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -72,6 +76,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.util.mockito.withArgCaptor
+import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
@@ -124,6 +129,7 @@
private val defaultLogoDescriptionFromActivityInfo = "Test Coke App"
private val logoDescriptionFromApp = "Test Cake App"
private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
+ private val authInteractionProperties = AuthInteractionProperties()
/** Prompt panel size padding */
private val smallHorizontalGuidelinePadding =
@@ -707,36 +713,66 @@
}
@Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
- val confirmHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(confirmHaptics?.hapticFeedbackConstant)
- .isEqualTo(
- if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
- else HapticFeedbackConstants.CONFIRM
- )
- assertThat(confirmHaptics?.flag)
- .isEqualTo(
- if (expectConfirmation) null
- else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
- )
+ val hapticsPreConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ if (expectConfirmation) {
+ assertThat(hapticsPreConfirm).isEqualTo(PromptViewModel.HapticsToPlay.None)
+ } else {
+ val confirmHaptics =
+ hapticsPreConfirm as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(confirmHaptics.constant)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+ assertThat(confirmHaptics.flag).isNull()
+ }
if (expectConfirmation) {
kosmos.promptViewModel.confirmAuthenticated()
}
- val confirmedHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(confirmedHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
- assertThat(confirmedHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ val hapticsPostConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val confirmedHaptics =
+ hapticsPostConfirm as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(confirmedHaptics.constant)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+ assertThat(confirmedHaptics.flag).isNull()
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun set_msdl_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
+ runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
+
+ val hapticsPreConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+
+ if (expectConfirmation) {
+ assertThat(hapticsPreConfirm).isEqualTo(PromptViewModel.HapticsToPlay.None)
+ } else {
+ val confirmHaptics = hapticsPreConfirm as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(confirmHaptics.token).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(confirmHaptics.properties).isEqualTo(authInteractionProperties)
+ }
+
+ if (expectConfirmation) {
+ kosmos.promptViewModel.confirmAuthenticated()
+ }
+
+ val hapticsPostConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val confirmedHaptics = hapticsPostConfirm as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(confirmedHaptics.token).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(confirmedHaptics.properties).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
@@ -745,21 +781,48 @@
kosmos.promptViewModel.confirmAuthenticated()
}
- val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
- assertThat(currentHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val currentHaptics = haptics as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(currentHaptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+ assertThat(currentHaptics.flag).isNull()
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playSuccessHaptic_SetsUnlockMSDLFeedback() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
+
+ if (expectConfirmation) {
+ kosmos.promptViewModel.confirmAuthenticated()
+ }
+
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val currentHaptics = haptics as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(currentHaptics.token).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(currentHaptics.properties).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false)
- val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
- assertThat(currentHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val currentHaptics = haptics as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(currentHaptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(currentHaptics.flag).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playErrorHaptic_SetsFailureMSDLFeedback() = runGenericTest {
+ kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false)
+
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val currentHaptics = haptics as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(currentHaptics.token).isEqualTo(MSDLToken.FAILURE)
+ assertThat(currentHaptics.properties).isEqualTo(authInteractionProperties)
}
// biometricprompt_sfps_fingerprint_authenticating reused across rotations
@@ -861,6 +924,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun set_haptic_on_errors() = runGenericTest {
kosmos.promptViewModel.showTemporaryError(
"so sad",
@@ -869,12 +933,30 @@
hapticFeedback = true,
)
- val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
- assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(haptics.flag).isNull()
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun set_msdl_haptic_on_errors() = runGenericTest {
+ kosmos.promptViewModel.showTemporaryError(
+ "so sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = true,
+ )
+
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(haptics.token).isEqualTo(MSDLToken.FAILURE)
+ assertThat(haptics.properties).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun plays_haptic_on_errors_unless_skipped() = runGenericTest {
kosmos.promptViewModel.showTemporaryError(
"still sad",
@@ -883,11 +965,26 @@
hapticFeedback = false,
)
- val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(hapticsToPlay).isEqualTo(PromptViewModel.HapticsToPlay.None)
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun plays_msdl_haptic_on_errors_unless_skipped() = runGenericTest {
+ kosmos.promptViewModel.showTemporaryError(
+ "still sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = false,
+ )
+
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(hapticsToPlay).isEqualTo(PromptViewModel.HapticsToPlay.None)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
@@ -899,15 +996,39 @@
hapticFeedback = true,
)
- val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.HapticConstant
if (expectConfirmation) {
- assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
- assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(haptics.flag).isNull()
} else {
- assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun plays_msdl_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
+
+ kosmos.promptViewModel.showTemporaryError(
+ "still sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = true,
+ )
+
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.MSDL
+ if (expectConfirmation) {
+ assertThat(haptics.token).isEqualTo(MSDLToken.FAILURE)
+ } else {
+ assertThat(haptics.token).isEqualTo(MSDLToken.UNLOCK)
+ }
+ assertThat(haptics.properties).isEqualTo(authInteractionProperties)
+ }
+
private suspend fun TestScope.showTemporaryErrors(
restart: Boolean,
helpAfterError: String = "",
@@ -1333,11 +1454,15 @@
@Test
fun switch_to_credential_fallback() = runGenericTest {
val size by collectLastValue(kosmos.promptViewModel.size)
+ val isShowingSfpsIndicator by collectLastValue(kosmos.sideFpsOverlayInteractor.isShowing)
// TODO(b/251476085): remove Spaghetti, migrate logic, and update this test
kosmos.promptViewModel.onSwitchToCredential()
assertThat(size).isEqualTo(PromptSize.LARGE)
+ if (testCase.modalities.hasSfps) {
+ assertThat(isShowingSfpsIndicator).isFalse()
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
new file mode 100644
index 0000000..22946c8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
@@ -0,0 +1,348 @@
+/*
+ * 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.bouncer.ui.composable
+
+import android.app.AlertDialog
+import android.platform.test.annotations.MotionTest
+import android.testing.TestableLooper.RunWithLooper
+import androidx.activity.BackEventCompat
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.isFinite
+import androidx.compose.ui.geometry.isUnspecified
+import androidx.compose.ui.semantics.SemanticsNode
+import androidx.compose.ui.test.junit4.AndroidComposeTestRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.Scale
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.isElement
+import com.android.compose.animation.scene.testing.lastAlphaForTesting
+import com.android.compose.animation.scene.testing.lastScaleForTesting
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerDialogFactory
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerUserActionsViewModel
+import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModel
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.motion.createSysUiComposeMotionTestRule
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.scene.shared.logger.sceneLogger
+import com.android.systemui.scene.shared.model.SceneContainerConfig
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
+import com.android.systemui.scene.ui.composable.Scene
+import com.android.systemui.scene.ui.composable.SceneContainer
+import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import com.android.systemui.scene.ui.viewmodel.splitEdgeDetector
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import org.json.JSONObject
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+import platform.test.motion.compose.ComposeFeatureCaptures.positionInRoot
+import platform.test.motion.compose.ComposeRecordingSpec
+import platform.test.motion.compose.MotionControl
+import platform.test.motion.compose.feature
+import platform.test.motion.compose.recordMotion
+import platform.test.motion.compose.runTest
+import platform.test.motion.golden.DataPoint
+import platform.test.motion.golden.DataPointType
+import platform.test.motion.golden.DataPointTypes
+import platform.test.motion.golden.FeatureCapture
+import platform.test.motion.golden.UnknownTypeException
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays.Phone
+
+/** MotionTest for the Bouncer Predictive Back animation */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@EnableSceneContainer
+@MotionTest
+class BouncerPredictiveBackTest : SysuiTestCase() {
+
+ private val deviceSpec = DeviceEmulationSpec(Phone)
+ private val kosmos = testKosmos()
+
+ @get:Rule val motionTestRule = createSysUiComposeMotionTestRule(kosmos, deviceSpec)
+ private val androidComposeTestRule =
+ motionTestRule.toolkit.composeContentTestRule as AndroidComposeTestRule<*, *>
+
+ private val sceneInteractor by lazy { kosmos.sceneInteractor }
+ private val Kosmos.sceneKeys by Fixture { listOf(Scenes.Lockscreen, Scenes.Bouncer) }
+ private val Kosmos.initialSceneKey by Fixture { Scenes.Bouncer }
+ private val Kosmos.sceneContainerConfig by Fixture {
+ val navigationDistances =
+ mapOf(
+ Scenes.Lockscreen to 1,
+ Scenes.Bouncer to 0,
+ )
+ SceneContainerConfig(sceneKeys, initialSceneKey, emptyList(), navigationDistances)
+ }
+
+ private val transitionState by lazy {
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey)
+ )
+ }
+ private val sceneContainerViewModel by lazy {
+ SceneContainerViewModel(
+ sceneInteractor = kosmos.sceneInteractor,
+ falsingInteractor = kosmos.falsingInteractor,
+ powerInteractor = kosmos.powerInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
+ splitEdgeDetector = kosmos.splitEdgeDetector,
+ logger = kosmos.sceneLogger,
+ motionEventHandlerReceiver = {},
+ )
+ .apply { setTransitionState(transitionState) }
+ }
+
+ private val bouncerDialogFactory =
+ object : BouncerDialogFactory {
+ override fun invoke(): AlertDialog {
+ throw AssertionError()
+ }
+ }
+ private val bouncerSceneActionsViewModelFactory =
+ object : BouncerUserActionsViewModel.Factory {
+ override fun create() = BouncerUserActionsViewModel(kosmos.bouncerInteractor)
+ }
+ private lateinit var bouncerSceneContentViewModel: BouncerSceneContentViewModel
+ private val bouncerSceneContentViewModelFactory =
+ object : BouncerSceneContentViewModel.Factory {
+ override fun create() = bouncerSceneContentViewModel
+ }
+ private val bouncerScene =
+ BouncerScene(
+ bouncerSceneActionsViewModelFactory,
+ bouncerSceneContentViewModelFactory,
+ bouncerDialogFactory
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ bouncerSceneContentViewModel = kosmos.bouncerSceneContentViewModel
+
+ val startable = kosmos.sceneContainerStartable
+ startable.start()
+ }
+
+ @Test
+ fun bouncerPredictiveBackMotion() =
+ motionTestRule.runTest {
+ val motion =
+ recordMotion(
+ content = { play ->
+ PlatformTheme {
+ BackGestureAnimation(play)
+ SceneContainer(
+ viewModel =
+ rememberViewModel("BouncerPredictiveBackTest") {
+ sceneContainerViewModel
+ },
+ sceneByKey =
+ mapOf(
+ Scenes.Lockscreen to FakeLockscreen(),
+ Scenes.Bouncer to bouncerScene
+ ),
+ initialSceneKey = Scenes.Bouncer,
+ overlayByKey = emptyMap(),
+ dataSourceDelegator = kosmos.sceneDataSourceDelegator
+ )
+ }
+ },
+ ComposeRecordingSpec(
+ MotionControl(
+ delayRecording = {
+ awaitCondition {
+ sceneInteractor.transitionState.value.isTransitioning()
+ }
+ }
+ ) {
+ awaitCondition {
+ sceneInteractor.transitionState.value.isIdle(Scenes.Lockscreen)
+ }
+ }
+ ) {
+ feature(isElement(Bouncer.Elements.Content), elementAlpha, "content_alpha")
+ feature(isElement(Bouncer.Elements.Content), elementScale, "content_scale")
+ feature(
+ isElement(Bouncer.Elements.Content),
+ positionInRoot,
+ "content_offset"
+ )
+ feature(
+ isElement(Bouncer.Elements.Background),
+ elementAlpha,
+ "background_alpha"
+ )
+ }
+ )
+
+ assertThat(motion).timeSeriesMatchesGolden()
+ }
+
+ @Composable
+ private fun BackGestureAnimation(play: Boolean) {
+ val backProgress = remember { Animatable(0f) }
+
+ LaunchedEffect(play) {
+ if (play) {
+ val dispatcher = androidComposeTestRule.activity.onBackPressedDispatcher
+ androidComposeTestRule.runOnUiThread {
+ dispatcher.dispatchOnBackStarted(backEvent())
+ }
+ backProgress.animateTo(
+ targetValue = 1f,
+ animationSpec = tween(durationMillis = 500)
+ ) {
+ androidComposeTestRule.runOnUiThread {
+ dispatcher.dispatchOnBackProgressed(
+ backEvent(progress = backProgress.value)
+ )
+ if (backProgress.value == 1f) {
+ dispatcher.onBackPressed()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun backEvent(progress: Float = 0f): BackEventCompat {
+ return BackEventCompat(
+ touchX = 0f,
+ touchY = 0f,
+ progress = progress,
+ swipeEdge = BackEventCompat.EDGE_LEFT,
+ )
+ }
+
+ private class FakeLockscreen : ExclusiveActivatable(), Scene {
+ override val key: SceneKey = Scenes.Lockscreen
+ override val userActions: Flow<Map<UserAction, UserActionResult>> = flowOf()
+
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ Box(modifier = modifier, contentAlignment = Alignment.Center) {
+ Text(text = "Fake Lockscreen")
+ }
+ }
+
+ override suspend fun onActivated() = awaitCancellation()
+ }
+
+ companion object {
+ private val elementAlpha =
+ FeatureCapture<SemanticsNode, Float>("alpha") {
+ DataPoint.of(it.lastAlphaForTesting, DataPointTypes.float)
+ }
+
+ private val elementScale =
+ FeatureCapture<SemanticsNode, Scale>("scale") {
+ DataPoint.of(it.lastScaleForTesting, scale)
+ }
+
+ private val scale: DataPointType<Scale> =
+ DataPointType(
+ "scale",
+ jsonToValue = {
+ when (it) {
+ "unspecified" -> Scale.Unspecified
+ "default" -> Scale.Default
+ "zero" -> Scale.Zero
+ is JSONObject -> {
+ val pivot = it.get("pivot")
+ Scale(
+ scaleX = it.getDouble("x").toFloat(),
+ scaleY = it.getDouble("y").toFloat(),
+ pivot =
+ when (pivot) {
+ "unspecified" -> Offset.Unspecified
+ "infinite" -> Offset.Infinite
+ is JSONObject ->
+ Offset(
+ pivot.getDouble("x").toFloat(),
+ pivot.getDouble("y").toFloat()
+ )
+ else -> throw UnknownTypeException()
+ }
+ )
+ }
+ else -> throw UnknownTypeException()
+ }
+ },
+ valueToJson = {
+ when (it) {
+ Scale.Unspecified -> "unspecified"
+ Scale.Default -> "default"
+ Scale.Zero -> "zero"
+ else -> {
+ JSONObject().apply {
+ put("x", it.scaleX)
+ put("y", it.scaleY)
+ put(
+ "pivot",
+ when {
+ it.pivot.isUnspecified -> "unspecified"
+ !it.pivot.isFinite -> "infinite"
+ else ->
+ JSONObject().apply {
+ put("x", it.pivot.x)
+ put("y", it.pivot.y)
+ }
+ }
+ )
+ }
+ }
+ }
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 88bfcf0..6377717 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -39,8 +39,6 @@
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -84,7 +82,6 @@
private AccessibilityManager mAccessibilityManager;
private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
- private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
private final FalsingClassifier.Result mFalsedResult =
FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), "");
@@ -107,10 +104,11 @@
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mFalsingDataProvider.isUnfolded()).thenReturn(false);
+ when(mFalsingDataProvider.isTouchScreenSource()).thenReturn(true);
mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
mMetricsLogger, mClassifiers, mSingleTapClassfier, mLongTapClassifier,
mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
- mAccessibilityManager, false, mFakeFeatureFlags);
+ mAccessibilityManager, false);
ArgumentCaptor<GestureFinalizedListener> gestureCompleteListenerCaptor =
@@ -120,7 +118,6 @@
gestureCompleteListenerCaptor.capture());
mGestureFinalizedListener = gestureCompleteListenerCaptor.getValue();
- mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index 6aecc0e..40b2a08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -63,6 +63,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -361,15 +362,88 @@
}
@Test
- fun faceAuthIsRequestedWhenQsExpansionStared() =
+ fun faceAuthIsRequestedWhenShadeExpansionStarted() =
testScope.runTest {
underTest.start()
- underTest.onQsExpansionStared()
+ underTest.onShadeExpansionStarted()
runCurrent()
assertThat(faceAuthRepository.runningAuthRequest.value)
- .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true))
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, false))
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun faceAuthIsRequestedWhenShadeExpansionIsStarted() =
+ testScope.runTest {
+ underTest.start()
+ faceAuthRepository.canRunFaceAuth.value = true
+ kosmos.sceneInteractor.snapToScene(toScene = Scenes.Lockscreen, "for-test")
+ runCurrent()
+
+ kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "for-test")
+ kosmos.sceneInteractor.setTransitionState(
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Shade,
+ currentScene = flowOf(Scenes.Lockscreen),
+ progress = MutableStateFlow(0.2f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ )
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, false))
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun faceAuthIsRequestedOnlyOnceWhenShadeExpansionStarts() =
+ testScope.runTest {
+ underTest.start()
+ faceAuthRepository.canRunFaceAuth.value = true
+ kosmos.sceneInteractor.snapToScene(toScene = Scenes.Lockscreen, "for-test")
+ runCurrent()
+
+ kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "for-test")
+ kosmos.sceneInteractor.setTransitionState(
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Shade,
+ currentScene = flowOf(Scenes.Lockscreen),
+ progress = MutableStateFlow(0.2f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ )
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, false))
+ faceAuthRepository.runningAuthRequest.value = null
+
+ // expansion progress shouldn't trigger face auth again
+ kosmos.sceneInteractor.setTransitionState(
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Shade,
+ currentScene = flowOf(Scenes.Lockscreen),
+ progress = MutableStateFlow(0.5f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ )
+
+ assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 0ac04b6..76539d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -467,6 +467,16 @@
assertThat(values.toIdSets()).containsExactly(setOf(0, 1, 2))
}
+ @Test
+ fun displayFlow_onlyDefaultDisplayAvailable_neverEmitsEmptySet() =
+ testScope.runTest {
+ setDisplays(0)
+
+ val values: List<Set<Display>> by collectValues(displayRepository.displays)
+
+ assertThat(values.toIdSets()).containsExactly(setOf(0))
+ }
+
private fun Iterable<Display>.ids(): List<Int> = map { it.displayId }
private fun Iterable<Set<Display>>.toIdSets(): List<Set<Int>> = map { it.ids().toSet() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
index 933ddb5..4a80d72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -21,7 +21,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.haptics.fakeVibratorHelper
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.fakeSystemClock
@@ -47,6 +47,7 @@
private val lowTickDuration = 12 // Mocked duration of a low tick
private val dragTextureThresholdMillis =
lowTickDuration * config.numberOfLowTicks + config.deltaMillisForDragInterval
+ private val vibratorHelper = kosmos.fakeVibratorHelper
private lateinit var sliderHapticFeedbackProvider: SliderHapticFeedbackProvider
@Before
@@ -56,11 +57,11 @@
whenever(velocityTracker.getAxisVelocity(config.velocityAxis))
.thenReturn(config.maxVelocityToScale)
- kosmos.vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_LOW_TICK] =
+ vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_LOW_TICK] =
lowTickDuration
sliderHapticFeedbackProvider =
SliderHapticFeedbackProvider(
- kosmos.vibratorHelper,
+ vibratorHelper,
velocityTracker,
config,
kosmos.fakeSystemClock,
@@ -136,7 +137,7 @@
sliderHapticFeedbackProvider.onUpperBookend()
sliderHapticFeedbackProvider.onUpperBookend()
- assertEquals(/* expected=*/ 1, vibratorHelper.timesVibratedWithEffect(vibration))
+ assertEquals(/* expected= */ 1, vibratorHelper.timesVibratedWithEffect(vibration))
}
@Test
@@ -162,7 +163,7 @@
sliderHapticFeedbackProvider.onProgress(progress)
// THEN the correct composition only plays once
- assertEquals(/* expected=*/ 1, vibratorHelper.timesVibratedWithEffect(ticks.compose()))
+ assertEquals(/* expected= */ 1, vibratorHelper.timesVibratedWithEffect(ticks.compose()))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt
new file mode 100644
index 0000000..9da6885
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.inputdevice.tutorial
+
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
+import com.android.systemui.testKosmos
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyboardTouchpadTutorialCoreStartableTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val broadcastDispatcher = kosmos.broadcastDispatcher
+ private val context = mock<Context>()
+ private val underTest =
+ KeyboardTouchpadTutorialCoreStartable(
+ { mock<TutorialNotificationCoordinator>() },
+ broadcastDispatcher,
+ context
+ )
+
+ @Test
+ fun registersBroadcastReceiverStartingActivityAsSystemUser() {
+ underTest.start()
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent("com.android.systemui.action.KEYBOARD_TOUCHPAD_TUTORIAL")
+ )
+
+ verify(context).startActivityAsUser(any(), eq(UserHandle.SYSTEM))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
new file mode 100644
index 0000000..945f953
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.inputdevice.tutorial.domain.interactor
+
+import android.app.Notification
+import android.app.NotificationManager
+import androidx.annotation.StringRes
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
+import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.hours
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TutorialNotificationCoordinatorTest : SysuiTestCase() {
+
+ private lateinit var underTest: TutorialNotificationCoordinator
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val keyboardRepository = FakeKeyboardRepository()
+ private val touchpadRepository = FakeTouchpadRepository()
+ private lateinit var dataStoreScope: CoroutineScope
+ private lateinit var repository: TutorialSchedulerRepository
+ @Mock private lateinit var notificationManager: NotificationManager
+ @Captor private lateinit var notificationCaptor: ArgumentCaptor<Notification>
+ @get:Rule val rule = MockitoJUnit.rule()
+
+ @Before
+ fun setup() {
+ dataStoreScope = CoroutineScope(Dispatchers.Unconfined)
+ repository =
+ TutorialSchedulerRepository(
+ context,
+ dataStoreScope,
+ dataStoreName = "TutorialNotificationCoordinatorTest"
+ )
+ val interactor =
+ TutorialSchedulerInteractor(keyboardRepository, touchpadRepository, repository)
+ underTest =
+ TutorialNotificationCoordinator(
+ testScope.backgroundScope,
+ context,
+ interactor,
+ notificationManager
+ )
+ notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
+ underTest.start()
+ }
+
+ @After
+ fun clear() {
+ runBlocking { repository.clearDataStore() }
+ dataStoreScope.cancel()
+ }
+
+ @Test
+ fun showKeyboardNotification() =
+ testScope.runTest {
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+ advanceTimeBy(LAUNCH_DELAY)
+ verifyNotification(
+ R.string.launch_keyboard_tutorial_notification_title,
+ R.string.launch_keyboard_tutorial_notification_content
+ )
+ }
+
+ @Test
+ fun showTouchpadNotification() =
+ testScope.runTest {
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+ advanceTimeBy(LAUNCH_DELAY)
+ verifyNotification(
+ R.string.launch_touchpad_tutorial_notification_title,
+ R.string.launch_touchpad_tutorial_notification_content
+ )
+ }
+
+ @Test
+ fun showKeyboardTouchpadNotification() =
+ testScope.runTest {
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+ advanceTimeBy(LAUNCH_DELAY)
+ verifyNotification(
+ R.string.launch_keyboard_touchpad_tutorial_notification_title,
+ R.string.launch_keyboard_touchpad_tutorial_notification_content
+ )
+ }
+
+ @Test
+ fun doNotShowNotification() =
+ testScope.runTest {
+ advanceTimeBy(LAUNCH_DELAY)
+ verify(notificationManager, never()).notify(eq(TAG), eq(NOTIFICATION_ID), any())
+ }
+
+ private fun verifyNotification(@StringRes titleResId: Int, @StringRes contentResId: Int) {
+ verify(notificationManager)
+ .notify(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture())
+ val notification = notificationCaptor.value
+ val actualTitle = notification.getString(Notification.EXTRA_TITLE)
+ val actualContent = notification.getString(Notification.EXTRA_TEXT)
+ assertThat(actualTitle).isEqualTo(context.getString(titleResId))
+ assertThat(actualContent).isEqualTo(context.getString(contentResId))
+ }
+
+ private fun Notification.getString(key: String): String =
+ this.extras?.getCharSequence(key).toString()
+
+ companion object {
+ private const val TAG = "TutorialSchedulerInteractor"
+ private const val NOTIFICATION_ID = 5566
+ private val LAUNCH_DELAY = 72.hours
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
index 432f7af..650f9dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
@@ -32,6 +32,8 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
@@ -63,13 +65,7 @@
dataStoreName = "TutorialSchedulerInteractorTest"
)
underTest =
- TutorialSchedulerInteractor(
- testScope.backgroundScope,
- keyboardRepository,
- touchpadRepository,
- schedulerRepository
- )
- underTest.start()
+ TutorialSchedulerInteractor(keyboardRepository, touchpadRepository, schedulerRepository)
}
@After
@@ -81,80 +77,90 @@
@Test
fun connectKeyboard_delayElapse_launchForKeyboard() =
testScope.runTest {
+ launchAndAssert(TutorialType.KEYBOARD)
+
keyboardRepository.setIsAnyKeyboardConnected(true)
advanceTimeBy(LAUNCH_DELAY)
- assertLaunch(TutorialType.KEYBOARD)
}
@Test
fun connectBothDevices_delayElapse_launchForBoth() =
testScope.runTest {
+ launchAndAssert(TutorialType.BOTH)
+
keyboardRepository.setIsAnyKeyboardConnected(true)
touchpadRepository.setIsAnyTouchpadConnected(true)
advanceTimeBy(LAUNCH_DELAY)
- assertLaunch(TutorialType.BOTH)
}
@Test
fun connectBothDevice_delayNotElapse_launchNothing() =
testScope.runTest {
+ launchAndAssert(TutorialType.NONE)
+
keyboardRepository.setIsAnyKeyboardConnected(true)
touchpadRepository.setIsAnyTouchpadConnected(true)
advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
- assertLaunch(TutorialType.NONE)
}
@Test
fun nothingConnect_delayElapse_launchNothing() =
testScope.runTest {
+ launchAndAssert(TutorialType.NONE)
+
keyboardRepository.setIsAnyKeyboardConnected(false)
touchpadRepository.setIsAnyTouchpadConnected(false)
advanceTimeBy(LAUNCH_DELAY)
- assertLaunch(TutorialType.NONE)
}
@Test
fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() =
testScope.runTest {
+ launchAndAssert(TutorialType.BOTH)
+
keyboardRepository.setIsAnyKeyboardConnected(true)
advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
touchpadRepository.setIsAnyTouchpadConnected(true)
advanceTimeBy(REMAINING_TIME)
- assertLaunch(TutorialType.BOTH)
}
@Test
fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() =
testScope.runTest {
+ launchAndAssert(TutorialType.NONE)
+
keyboardRepository.setIsAnyKeyboardConnected(true)
advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
touchpadRepository.setIsAnyTouchpadConnected(true)
keyboardRepository.setIsAnyKeyboardConnected(false)
advanceTimeBy(REMAINING_TIME)
- assertLaunch(TutorialType.NONE)
}
- // TODO: likely to be changed after we update TutorialSchedulerInteractor.launchTutorial
- private suspend fun assertLaunch(tutorialType: TutorialType) {
- when (tutorialType) {
- TutorialType.KEYBOARD -> {
- assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
- assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
- }
- TutorialType.TOUCHPAD -> {
- assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
- assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
- }
- TutorialType.BOTH -> {
- assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
- assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
- }
- TutorialType.NONE -> {
- assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
- assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
+ private suspend fun launchAndAssert(expectedTutorial: TutorialType) =
+ testScope.backgroundScope.launch {
+ val actualTutorial = underTest.tutorials.first()
+ assertThat(actualTutorial).isEqualTo(expectedTutorial)
+
+ // TODO: need to update after we move launch into the tutorial
+ when (expectedTutorial) {
+ TutorialType.KEYBOARD -> {
+ assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
+ assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
+ }
+ TutorialType.TOUCHPAD -> {
+ assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
+ assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
+ }
+ TutorialType.BOTH -> {
+ assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
+ assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
+ }
+ TutorialType.NONE -> {
+ assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
+ assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
+ }
}
}
- }
companion object {
private val LAUNCH_DELAY = 72.hours
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 9d9e5be6..3ccb989 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -34,14 +34,16 @@
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -57,29 +59,28 @@
@RunWith(AndroidJUnit4::class)
class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
- private val dispatcher = StandardTestDispatcher()
- private val testScope = TestScope(dispatcher)
+ private val kosmos = testKosmos()
+ private val dispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
private lateinit var viewModel: StickyKeysIndicatorViewModel
private val inputManager = mock<InputManager>()
private val keyboardRepository = FakeKeyboardRepository()
- private val secureSettings = FakeSettings()
+ private val secureSettings = kosmos.fakeSettings
private val userRepository = Kosmos().fakeUserRepository
private val captor =
ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
@Before
fun setup() {
- val settingsRepository = UserAwareSecureSettingsRepositoryImpl(
- secureSettings,
- userRepository,
- dispatcher
- )
- val stickyKeysRepository = StickyKeysRepositoryImpl(
- inputManager,
- dispatcher,
- settingsRepository,
- mock<StickyKeysLogger>()
- )
+ val settingsRepository =
+ UserAwareSecureSettingsRepositoryImpl(secureSettings, userRepository, dispatcher)
+ val stickyKeysRepository =
+ StickyKeysRepositoryImpl(
+ inputManager,
+ dispatcher,
+ settingsRepository,
+ mock<StickyKeysLogger>()
+ )
setStickyKeySetting(enabled = false)
viewModel =
StickyKeysIndicatorViewModel(
@@ -182,16 +183,16 @@
val stickyKeys by collectLastValue(viewModel.indicatorContent)
setStickyKeysActive()
- setStickyKeys(mapOf(
- ALT to false,
- META to false,
- SHIFT to false))
+ setStickyKeys(mapOf(ALT to false, META to false, SHIFT to false))
- assertThat(stickyKeys).isEqualTo(mapOf(
- ALT to Locked(false),
- META to Locked(false),
- SHIFT to Locked(false),
- ))
+ assertThat(stickyKeys)
+ .isEqualTo(
+ mapOf(
+ ALT to Locked(false),
+ META to Locked(false),
+ SHIFT to Locked(false),
+ )
+ )
}
}
@@ -201,9 +202,7 @@
val stickyKeys by collectLastValue(viewModel.indicatorContent)
setStickyKeysActive()
- setStickyKeys(mapOf(
- ALT to false,
- ALT to true))
+ setStickyKeys(mapOf(ALT to false, ALT to true))
assertThat(stickyKeys).isEqualTo(mapOf(ALT to Locked(true)))
}
@@ -215,17 +214,23 @@
val stickyKeys by collectLastValue(viewModel.indicatorContent)
setStickyKeysActive()
- setStickyKeys(mapOf(
- META to false,
- SHIFT to false, // shift is sticky but not locked
- CTRL to false))
+ setStickyKeys(
+ mapOf(
+ META to false,
+ SHIFT to false, // shift is sticky but not locked
+ CTRL to false
+ )
+ )
val previousShiftIndex = stickyKeys?.toList()?.indexOf(SHIFT to Locked(false))
- setStickyKeys(mapOf(
- SHIFT to false,
- SHIFT to true, // shift is now locked
- META to false,
- CTRL to false))
+ setStickyKeys(
+ mapOf(
+ SHIFT to false,
+ SHIFT to true, // shift is now locked
+ META to false,
+ CTRL to false
+ )
+ )
assertThat(stickyKeys?.toList()?.indexOf(SHIFT to Locked(true)))
.isEqualTo(previousShiftIndex)
}
@@ -247,17 +252,27 @@
StickyModifierState() {
private fun isOn(key: ModifierKey) = keys.any { it.key == key && !it.value }
+
private fun isLocked(key: ModifierKey) = keys.any { it.key == key && it.value }
override fun isAltGrModifierLocked() = isLocked(ALT_GR)
+
override fun isAltGrModifierOn() = isOn(ALT_GR)
+
override fun isAltModifierLocked() = isLocked(ALT)
+
override fun isAltModifierOn() = isOn(ALT)
+
override fun isCtrlModifierLocked() = isLocked(CTRL)
+
override fun isCtrlModifierOn() = isOn(CTRL)
+
override fun isMetaModifierLocked() = isLocked(META)
+
override fun isMetaModifierOn() = isOn(META)
+
override fun isShiftModifierLocked() = isLocked(SHIFT)
+
override fun isShiftModifierOn() = isOn(SHIFT)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 29cd9a2..fa69fdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -50,6 +50,8 @@
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRendererFactory
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -64,12 +66,12 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
@@ -87,6 +89,11 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class CustomizationProviderTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.unconfinedTestDispatcher
+ private val testScope = kosmos.unconfinedTestScope
+ private val fakeSettings = kosmos.unconfinedDispatcherFakeSettings
+
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var userTracker: UserTracker
@@ -104,9 +111,6 @@
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var underTest: CustomizationProvider
- private lateinit var testScope: TestScope
-
- private val kosmos = testKosmos()
@Before
fun setUp() {
@@ -120,8 +124,6 @@
biometricSettingsRepository = FakeBiometricSettingsRepository()
underTest = CustomizationProvider()
- val testDispatcher = UnconfinedTestDispatcher()
- testScope = TestScope(testDispatcher)
val localUserSelectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
@@ -170,7 +172,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = fakeSettings,
selectionsManager = localUserSelectionManager,
),
dumpManager = mock(),
@@ -216,7 +218,7 @@
mainDispatcher = testDispatcher,
backgroundHandler = backgroundHandler,
)
- underTest.mainDispatcher = UnconfinedTestDispatcher()
+ underTest.mainDispatcher = testDispatcher
underTest.attachInfoForTesting(
context,
@@ -319,6 +321,7 @@
),
)
)
+ runCurrent()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
index af5187d..1e9db64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
@@ -25,15 +25,14 @@
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlin.test.Test
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestCoroutineScheduler
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -44,12 +43,12 @@
@SmallTest
class KeyguardClockRepositoryTest : SysuiTestCase() {
- private lateinit var scheduler: TestCoroutineScheduler
- private lateinit var dispatcher: CoroutineDispatcher
- private lateinit var scope: TestScope
+ private val kosmos = testKosmos()
+ private val dispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
+ private val fakeSettings = kosmos.fakeSettings
private lateinit var underTest: KeyguardClockRepository
- private lateinit var fakeSettings: FakeSettings
@Mock private lateinit var clockRegistry: ClockRegistry
@Mock private lateinit var clockEventController: ClockEventController
private val fakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
@@ -57,10 +56,6 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- fakeSettings = FakeSettings()
- scheduler = TestCoroutineScheduler()
- dispatcher = StandardTestDispatcher(scheduler)
- scope = TestScope(dispatcher)
underTest =
KeyguardClockRepositoryImpl(
fakeSettings,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
index 8b8a6cb..5a597fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
@@ -21,14 +21,12 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
import com.android.systemui.settings.FakeUserTracker
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlin.test.Test
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestCoroutineScheduler
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -38,23 +36,18 @@
@SmallTest
class KeyguardSmartspaceRepositoryImplTest : SysuiTestCase() {
- private lateinit var scheduler: TestCoroutineScheduler
- private lateinit var dispatcher: CoroutineDispatcher
- private lateinit var scope: TestScope
+ private val kosmos = testKosmos()
+ private val scope = kosmos.testScope
+ private val fakeSettings = kosmos.fakeSettings
private lateinit var underTest: KeyguardSmartspaceRepository
- private lateinit var fakeSettings: FakeSettings
private lateinit var fakeUserTracker: FakeUserTracker
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- fakeSettings = FakeSettings()
fakeUserTracker = FakeUserTracker()
fakeSettings.userId = fakeUserTracker.userId
- scheduler = TestCoroutineScheduler()
- dispatcher = StandardTestDispatcher(scheduler)
- scope = TestScope(dispatcher)
underTest =
KeyguardSmartspaceRepositoryImpl(
context = context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index 3cbbb64..bea415c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -22,8 +22,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
@@ -42,6 +42,7 @@
import com.android.systemui.scene.domain.resolver.notifShadeSceneFamilyResolver
import com.android.systemui.scene.domain.resolver.quickSettingsSceneFamilyResolver
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -77,12 +78,14 @@
transitionInteractor = kosmos.keyguardTransitionInteractor,
dismissInteractor = dismissInteractor,
applicationScope = testScope.backgroundScope,
- sceneInteractor = kosmos.sceneInteractor,
- deviceEntryInteractor = kosmos.deviceEntryInteractor,
- quickSettingsSceneFamilyResolver = kosmos.quickSettingsSceneFamilyResolver,
- notifShadeSceneFamilyResolver = kosmos.notifShadeSceneFamilyResolver,
+ sceneInteractor = { kosmos.sceneInteractor },
+ deviceEntryInteractor = { kosmos.deviceEntryInteractor },
+ quickSettingsSceneFamilyResolver = { kosmos.quickSettingsSceneFamilyResolver },
+ notifShadeSceneFamilyResolver = { kosmos.notifShadeSceneFamilyResolver },
powerInteractor = kosmos.powerInteractor,
alternateBouncerInteractor = kosmos.alternateBouncerInteractor,
+ keyguardInteractor = { kosmos.keyguardInteractor },
+ shadeInteractor = { kosmos.shadeInteractor },
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 7cc9185..bfb8a57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
@@ -86,6 +87,7 @@
{ mock(DeviceEntryBackgroundViewModel::class.java) },
{ falsingManager },
{ mock(VibratorHelper::class.java) },
+ logcatLogBuffer(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index d13419e..1929cd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -54,6 +54,8 @@
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -69,13 +71,11 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -93,6 +93,11 @@
@RunWith(ParameterizedAndroidJunit4::class)
class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@Mock private lateinit var expandable: Expandable
@Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@@ -108,7 +113,6 @@
private lateinit var underTest: KeyguardBottomAreaViewModel
- private lateinit var testScope: TestScope
private lateinit var repository: FakeKeyguardRepository
private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
@@ -116,8 +120,6 @@
private lateinit var dockManager: DockManagerFake
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
- private val kosmos = testKosmos()
-
init {
mSetFlagsRule.setFlagsParameterization(flags)
}
@@ -162,8 +164,6 @@
whenever(userTracker.userHandle).thenReturn(mock())
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
- val testDispatcher = StandardTestDispatcher()
- testScope = TestScope(testDispatcher)
val localUserSelectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
@@ -199,7 +199,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = settings,
selectionsManager = localUserSelectionManager,
),
configs =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 07f7557..720f2e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -72,7 +72,7 @@
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlin.math.min
import kotlin.test.assertEquals
@@ -94,6 +94,10 @@
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var expandable: Expandable
@@ -151,11 +155,8 @@
private lateinit var glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel
- private val kosmos = testKosmos()
-
private lateinit var underTest: KeyguardQuickAffordancesCombinedViewModel
- private val testScope = kosmos.testScope
private lateinit var repository: FakeKeyguardRepository
private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
@@ -244,7 +245,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = kosmos.testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = settings,
selectionsManager = localUserSelectionManager,
),
configs =
@@ -403,7 +404,7 @@
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_NEW_PICKER_UI)
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
fun startButton_inPreviewMode_onPreviewQuickAffordanceSelected() =
testScope.runTest {
underTest.onPreviewSlotSelected(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
index e55cb12..030b172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
@@ -19,8 +19,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogcatEchoTrackerAlways
import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
@@ -57,9 +57,7 @@
MAX_SIZE,
BUFFER_NAME,
systemClock,
- mock(),
- testDispatcher,
- testScope.backgroundScope,
+ LogcatEchoTrackerAlways(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
index 8c62bc2..dfd964f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
@@ -20,25 +20,20 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.log.LogcatEchoTrackerAlways
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class TableLogBufferFactoryTest : SysuiTestCase() {
private val dumpManager: DumpManager = mock()
private val systemClock = FakeSystemClock()
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
private val underTest =
- TableLogBufferFactory(dumpManager, systemClock, mock(), testDispatcher, testScope)
+ TableLogBufferFactory(dumpManager, systemClock, LogcatEchoTrackerAlways())
@Test
fun create_alwaysCreatesNewInstance() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index ace562b..9c4c862 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -23,22 +23,18 @@
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
import com.android.systemui.log.table.TableChange.Companion.MAX_STRING_LENGTH
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class TableLogBufferTest : SysuiTestCase() {
@@ -49,9 +45,6 @@
private lateinit var logcatEchoTracker: LogcatEchoTracker
private lateinit var localLogcat: FakeLogProxy
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
-
@Before
fun setup() {
localLogcat = FakeLogProxy()
@@ -65,8 +58,6 @@
NAME,
systemClock,
logcatEchoTracker,
- testDispatcher,
- testScope.backgroundScope,
localLogcat = localLogcat,
)
}
@@ -78,8 +69,6 @@
"name",
systemClock,
logcatEchoTracker,
- testDispatcher,
- testScope.backgroundScope,
localLogcat = localLogcat,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index fd53b5ba..823a23d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -38,6 +38,8 @@
import android.media.session.PlaybackState
import android.net.Uri
import android.os.Bundle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.service.notification.StatusBarNotification
@@ -56,12 +58,13 @@
import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS
import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS
-import com.android.systemui.flags.Flags.MEDIA_SESSION_ACTIONS
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.mediaLogger
+import com.android.systemui.media.controls.shared.mockMediaLogger
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
import com.android.systemui.media.controls.shared.model.MediaData
@@ -70,7 +73,6 @@
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.controls.util.fakeMediaControllerFactory
import com.android.systemui.media.controls.util.mediaFlags
-import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.testKosmos
@@ -187,11 +189,10 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().apply { mediaLogger = mockMediaLogger }
private val testDispatcher = kosmos.testDispatcher
private val testScope = kosmos.testScope
private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
- private val activityStarter = kosmos.activityStarter
private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
@@ -241,7 +242,6 @@
mediaDeviceManager = mediaDeviceManager,
mediaDataCombineLatest = mediaDataCombineLatest,
mediaDataFilter = mediaDataFilter,
- activityStarter = activityStarter,
smartspaceMediaDataProvider = smartspaceMediaDataProvider,
useMediaResumption = true,
useQsMediaPlayer = true,
@@ -252,6 +252,7 @@
smartspaceManager = smartspaceManager,
keyguardUpdateMonitor = keyguardUpdateMonitor,
mediaDataLoader = { kosmos.mediaDataLoader },
+ mediaLogger = kosmos.mediaLogger,
)
verify(tunerService)
.addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -317,7 +318,6 @@
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(SMARTSPACE_CREATION_TIME)
whenever(mediaSmartspaceTarget.expiryTimeMillis).thenReturn(SMARTSPACE_EXPIRY_TIME)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, false)
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false)
fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false)
fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false)
@@ -969,7 +969,8 @@
assertThat(data.resumption).isTrue()
assertThat(data.song).isEqualTo(SESSION_TITLE)
assertThat(data.app).isEqualTo(APP_NAME)
- assertThat(data.actions).hasSize(1)
+ // resume button is a semantic action.
+ assertThat(data.actions).hasSize(0)
assertThat(data.semanticActions!!.playOrPause).isNotNull()
assertThat(data.lastActive).isAtLeast(currentTime)
verify(logger).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
@@ -996,7 +997,8 @@
assertThat(data.resumption).isTrue()
assertThat(data.song).isEqualTo(SESSION_TITLE)
assertThat(data.app).isEqualTo(APP_NAME)
- assertThat(data.actions).hasSize(1)
+ // resume button is a semantic action.
+ assertThat(data.actions).hasSize(0)
assertThat(data.semanticActions!!.playOrPause).isNotNull()
assertThat(data.lastActive).isAtLeast(currentTime)
assertThat(data.isExplicit).isTrue()
@@ -1671,7 +1673,6 @@
@Test
fun testPlaybackActions_noState_usesNotification() {
val desc = "Notification Action"
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
whenever(controller.playbackState).thenReturn(null)
val notifWithAction =
@@ -1705,7 +1706,6 @@
@Test
fun testPlaybackActions_hasPrevNext() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions =
PlaybackState.ACTION_PLAY or
PlaybackState.ACTION_SKIP_TO_PREVIOUS or
@@ -1749,7 +1749,6 @@
@Test
fun testPlaybackActions_noPrevNext_usesCustom() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
customDesc.forEach {
@@ -1781,7 +1780,6 @@
@Test
fun testPlaybackActions_connecting() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder =
PlaybackState.Builder()
@@ -1802,7 +1800,6 @@
@Test
fun testPlaybackActions_reservedSpace() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
customDesc.forEach {
@@ -1840,7 +1837,6 @@
@Test
fun testPlaybackActions_playPause_hasButton() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY_PAUSE
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
whenever(controller.playbackState).thenReturn(stateBuilder.build())
@@ -1939,7 +1935,6 @@
@Test
fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build()
whenever(controller.playbackState).thenReturn(state)
@@ -2161,7 +2156,6 @@
@Test
fun testRetain_sessionPlayer_notifRemoved_doesNotChange() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2181,7 +2175,6 @@
@Test
fun testRetain_sessionPlayer_sessionDestroyed_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2215,7 +2208,6 @@
@Test
fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions is added, and then the session is destroyed
@@ -2235,7 +2227,6 @@
@Test
fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions and that does allow resumption is added,
@@ -2268,7 +2259,6 @@
@Test
fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2295,7 +2285,6 @@
@Test
fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions is added, and then the session is destroyed
@@ -2314,7 +2303,6 @@
@Test
fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions and that does allow resumption is added,
@@ -2348,7 +2336,6 @@
@Test
fun testSessionDestroyed_noNotificationKey_stillRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
// When a notiifcation is added and then removed before it is fully processed
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
@@ -2419,6 +2406,45 @@
assertThat(mediaDataCaptor.value.artwork).isNull()
}
+ @Test
+ @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
+ fun postDuplicateNotification_doesNotCallListeners() {
+ addNotificationAndLoad()
+ reset(listener)
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+
+ testScope.assertRunAllReady(foreground = 0, background = 1)
+ verify(listener, never())
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ verify(kosmos.mediaLogger).logDuplicateMediaNotification(eq(KEY))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
+ fun postDuplicateNotification_callsListeners() {
+ addNotificationAndLoad()
+ reset(listener)
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ testScope.assertRunAllReady(foreground = 1, background = 1)
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ verify(kosmos.mediaLogger, never()).logDuplicateMediaNotification(eq(KEY))
+ }
+
private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) {
runCurrent()
if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 9eccd9f..4cf7de3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -61,7 +61,6 @@
import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS
import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS
-import com.android.systemui.flags.Flags.MEDIA_SESSION_ACTIONS
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -70,6 +69,8 @@
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.mediaLogger
+import com.android.systemui.media.controls.shared.mockMediaLogger
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
import com.android.systemui.media.controls.shared.model.MediaData
@@ -84,7 +85,7 @@
import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -141,6 +142,11 @@
@RunWith(ParameterizedAndroidJunit4::class)
@EnableSceneContainer
class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
+ private val kosmos = testKosmos().apply { mediaLogger = mockMediaLogger }
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock lateinit var controller: MediaController
@Mock lateinit var transportControls: MediaController.TransportControls
@@ -193,9 +199,6 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
- private val kosmos = testKosmos()
- private val testDispatcher = kosmos.testDispatcher
- private val testScope = kosmos.testScope
private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
private val activityStarter = kosmos.activityStarter
private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
@@ -203,7 +206,6 @@
private val mediaFilterRepository = kosmos.mediaFilterRepository
private val mediaDataFilter = kosmos.mediaDataFilter
- private val settings = FakeSettings()
private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
private val originalSmartspaceSetting =
@@ -257,6 +259,7 @@
keyguardUpdateMonitor = keyguardUpdateMonitor,
mediaDataRepository = kosmos.mediaDataRepository,
mediaDataLoader = { kosmos.mediaDataLoader },
+ mediaLogger = kosmos.mediaLogger,
)
mediaDataProcessor.start()
testScope.runCurrent()
@@ -337,7 +340,6 @@
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(SMARTSPACE_CREATION_TIME)
whenever(mediaSmartspaceTarget.expiryTimeMillis).thenReturn(SMARTSPACE_EXPIRY_TIME)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, false)
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false)
fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false)
fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false)
@@ -985,7 +987,8 @@
assertThat(data.resumption).isTrue()
assertThat(data.song).isEqualTo(SESSION_TITLE)
assertThat(data.app).isEqualTo(APP_NAME)
- assertThat(data.actions).hasSize(1)
+ // resume button is a semantic action.
+ assertThat(data.actions).hasSize(0)
assertThat(data.semanticActions!!.playOrPause).isNotNull()
assertThat(data.lastActive).isAtLeast(currentTime)
verify(logger).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
@@ -1012,7 +1015,8 @@
assertThat(data.resumption).isTrue()
assertThat(data.song).isEqualTo(SESSION_TITLE)
assertThat(data.app).isEqualTo(APP_NAME)
- assertThat(data.actions).hasSize(1)
+ // resume button is a semantic action.
+ assertThat(data.actions).hasSize(0)
assertThat(data.semanticActions!!.playOrPause).isNotNull()
assertThat(data.lastActive).isAtLeast(currentTime)
assertThat(data.isExplicit).isTrue()
@@ -1679,7 +1683,6 @@
@Test
fun testPlaybackActions_noState_usesNotification() {
val desc = "Notification Action"
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
whenever(controller.playbackState).thenReturn(null)
val notifWithAction =
@@ -1713,7 +1716,6 @@
@Test
fun testPlaybackActions_hasPrevNext() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions =
PlaybackState.ACTION_PLAY or
PlaybackState.ACTION_SKIP_TO_PREVIOUS or
@@ -1757,7 +1759,6 @@
@Test
fun testPlaybackActions_noPrevNext_usesCustom() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
customDesc.forEach {
@@ -1789,7 +1790,6 @@
@Test
fun testPlaybackActions_connecting() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder =
PlaybackState.Builder()
@@ -1810,7 +1810,6 @@
@Test
@EnableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
fun postWithPlaybackActions_drawablesReused() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val stateActions =
@@ -1835,10 +1834,6 @@
assertThat(userEntries).hasSize(1)
val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!!
- assertThat(secondSemanticActions.playOrPause?.icon)
- .isEqualTo(firstSemanticActions.playOrPause?.icon)
- assertThat(secondSemanticActions.playOrPause?.background)
- .isEqualTo(firstSemanticActions.playOrPause?.background)
assertThat(secondSemanticActions.nextOrCustom?.icon)
.isEqualTo(firstSemanticActions.nextOrCustom?.icon)
assertThat(secondSemanticActions.prevOrCustom?.icon)
@@ -1848,7 +1843,6 @@
@Test
@DisableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
fun postWithPlaybackActions_drawablesNotReused() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val stateActions =
@@ -1873,11 +1867,6 @@
assertThat(userEntries).hasSize(1)
val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!!
-
- assertThat(secondSemanticActions.playOrPause?.icon)
- .isNotEqualTo(firstSemanticActions.playOrPause?.icon)
- assertThat(secondSemanticActions.playOrPause?.background)
- .isNotEqualTo(firstSemanticActions.playOrPause?.background)
assertThat(secondSemanticActions.nextOrCustom?.icon)
.isNotEqualTo(firstSemanticActions.nextOrCustom?.icon)
assertThat(secondSemanticActions.prevOrCustom?.icon)
@@ -1887,7 +1876,6 @@
@Test
fun testPlaybackActions_reservedSpace() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
customDesc.forEach {
@@ -1925,7 +1913,6 @@
@Test
fun testPlaybackActions_playPause_hasButton() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY_PAUSE
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
whenever(controller.playbackState).thenReturn(stateBuilder.build())
@@ -2024,7 +2011,6 @@
@Test
fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build()
whenever(controller.playbackState).thenReturn(state)
@@ -2245,7 +2231,6 @@
@Test
fun testRetain_sessionPlayer_notifRemoved_doesNotChange() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2265,7 +2250,6 @@
@Test
fun testRetain_sessionPlayer_sessionDestroyed_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2299,7 +2283,6 @@
@Test
fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions is added, and then the session is destroyed
@@ -2319,7 +2302,6 @@
@Test
fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions and that does allow resumption is added,
@@ -2352,7 +2334,6 @@
@Test
fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2379,7 +2360,6 @@
@Test
fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions is added, and then the session is destroyed
@@ -2398,7 +2378,6 @@
@Test
fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions and that does allow resumption is added,
@@ -2432,7 +2411,6 @@
@Test
fun testSessionDestroyed_noNotificationKey_stillRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
// When a notiifcation is added and then removed before it is fully processed
mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
@@ -2503,6 +2481,55 @@
assertThat(mediaDataCaptor.value.artwork).isNull()
}
+ @Test
+ @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
+ fun postDuplicateNotification_doesNotCallListeners() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+
+ mediaDataProcessor.addInternalListener(mediaDataFilter)
+ mediaDataFilter.mediaDataProcessor = mediaDataProcessor
+ addNotificationAndLoad()
+ reset(listener)
+ mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
+
+ testScope.assertRunAllReady(foreground = 0, background = 1)
+ verify(listener, never())
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ verify(kosmos.mediaLogger).logDuplicateMediaNotification(eq(KEY))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
+ fun postDuplicateNotification_callsListeners() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+
+ mediaDataProcessor.addInternalListener(mediaDataFilter)
+ mediaDataFilter.mediaDataProcessor = mediaDataProcessor
+ addNotificationAndLoad()
+ reset(listener)
+ mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
+ testScope.assertRunAllReady(foreground = 1, background = 1)
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ verify(kosmos.mediaLogger, never()).logDuplicateMediaNotification(eq(KEY))
+ }
+
private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) {
runCurrent()
if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index c1bba4d..680df15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -72,7 +72,6 @@
@Mock private lateinit var mediaController: MediaController
@Mock private lateinit var logger: MediaTimeoutLogger
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
- private lateinit var executor: FakeExecutor
@Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit
@Mock private lateinit var stateCallback: (String, PlaybackState) -> Unit
@Mock private lateinit var sessionCallback: (String) -> Unit
@@ -88,6 +87,9 @@
private lateinit var resumeData: MediaData
private lateinit var mediaTimeoutListener: MediaTimeoutListener
private var clock = FakeSystemClock()
+ private lateinit var mainExecutor: FakeExecutor
+ private lateinit var bgExecutor: FakeExecutor
+ private lateinit var uiExecutor: FakeExecutor
@Mock private lateinit var mediaFlags: MediaFlags
@Mock private lateinit var smartspaceData: SmartspaceMediaData
@@ -95,11 +97,15 @@
fun setup() {
whenever(mediaControllerFactory.create(any())).thenReturn(mediaController)
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
- executor = FakeExecutor(clock)
+ mainExecutor = FakeExecutor(clock)
+ bgExecutor = FakeExecutor(clock)
+ uiExecutor = FakeExecutor(clock)
mediaTimeoutListener =
MediaTimeoutListener(
mediaControllerFactory,
- executor,
+ bgExecutor,
+ uiExecutor,
+ mainExecutor,
logger,
statusBarStateController,
clock,
@@ -143,30 +149,31 @@
whenever(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
whenever(mediaController.playbackState).thenReturn(playingState)
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ loadMediaData(KEY, null, mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
verify(logger).logPlaybackState(eq(KEY), eq(playingState))
// Ignores if same key
clearInvocations(mediaController)
- mediaTimeoutListener.onMediaDataLoaded(KEY, KEY, mediaData)
+ loadMediaData(KEY, KEY, mediaData)
verify(mediaController, never()).registerCallback(anyObject())
}
@Test
fun testOnMediaDataLoaded_registersTimeout_whenPaused() {
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ loadMediaData(KEY, null, mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
verify(logger).logScheduleTimeout(eq(KEY), eq(false), eq(false))
- assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
+ assertThat(mainExecutor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
}
@Test
fun testOnMediaDataRemoved_unregistersPlaybackListener() {
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ loadMediaData(KEY, null, mediaData)
mediaTimeoutListener.onMediaDataRemoved(KEY, false)
+ assertThat(bgExecutor.runAllReady()).isEqualTo(1)
verify(mediaController).unregisterCallback(anyObject())
// Ignores duplicate requests
@@ -178,50 +185,50 @@
@Test
fun testOnMediaDataRemoved_clearsTimeout() {
// GIVEN media that is paused
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
- assertThat(executor.numPending()).isEqualTo(1)
+ loadMediaData(KEY, null, mediaData)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
// WHEN the media is removed
mediaTimeoutListener.onMediaDataRemoved(KEY, false)
// THEN the timeout runnable is cancelled
- assertThat(executor.numPending()).isEqualTo(0)
+ assertThat(mainExecutor.numPending()).isEqualTo(0)
}
@Test
fun testOnMediaDataLoaded_migratesKeys() {
val newKey = "NEWKEY"
// From not playing
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ loadMediaData(KEY, null, mediaData)
clearInvocations(mediaController)
// To playing
val playingState = mock(android.media.session.PlaybackState::class.java)
whenever(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
whenever(mediaController.playbackState).thenReturn(playingState)
- mediaTimeoutListener.onMediaDataLoaded(newKey, KEY, mediaData)
+ loadMediaData(newKey, KEY, mediaData)
verify(mediaController).unregisterCallback(anyObject())
verify(mediaController).registerCallback(anyObject())
verify(logger).logMigrateListener(eq(KEY), eq(newKey), eq(true))
// Enqueues callback
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
}
@Test
fun testOnMediaDataLoaded_migratesKeys_noTimeoutExtension() {
val newKey = "NEWKEY"
// From not playing
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ loadMediaData(KEY, null, mediaData)
clearInvocations(mediaController)
// Migrate, still not playing
val playingState = mock(android.media.session.PlaybackState::class.java)
whenever(playingState.state).thenReturn(PlaybackState.STATE_PAUSED)
whenever(mediaController.playbackState).thenReturn(playingState)
- mediaTimeoutListener.onMediaDataLoaded(newKey, KEY, mediaData)
+ loadMediaData(newKey, KEY, mediaData)
// The number of queued timeout tasks remains the same. The timeout task isn't cancelled nor
// is another scheduled
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
verify(logger).logUpdateListener(eq(newKey), eq(false))
}
@@ -233,8 +240,8 @@
mediaCallbackCaptor.value.onPlaybackStateChanged(
PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()
)
- assertThat(executor.numPending()).isEqualTo(1)
- assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
}
@Test
@@ -245,7 +252,7 @@
mediaCallbackCaptor.value.onPlaybackStateChanged(
PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 0f).build()
)
- assertThat(executor.numPending()).isEqualTo(0)
+ assertThat(mainExecutor.numPending()).isEqualTo(0)
verify(logger).logTimeoutCancelled(eq(KEY), any())
}
@@ -257,7 +264,7 @@
mediaCallbackCaptor.value.onPlaybackStateChanged(
PlaybackState.Builder().setState(PlaybackState.STATE_STOPPED, 0L, 0f).build()
)
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
}
@Test
@@ -265,7 +272,7 @@
// Assuming we're have a pending timeout
testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
- with(executor) {
+ with(mainExecutor) {
advanceClockToNext()
runAllReady()
}
@@ -274,7 +281,7 @@
@Test
fun testIsTimedOut() {
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ loadMediaData(KEY, null, mediaData)
assertThat(mediaTimeoutListener.isTimedOut(KEY)).isFalse()
}
@@ -282,16 +289,17 @@
fun testOnSessionDestroyed_active_clearsTimeout() {
// GIVEN media that is paused
val mediaPaused = mediaData.copy(isPlaying = false)
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaPaused)
+ loadMediaData(KEY, null, mediaPaused)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
// WHEN the session is destroyed
mediaCallbackCaptor.value.onSessionDestroyed()
// THEN the controller is unregistered and timeout run
+ assertThat(bgExecutor.runAllReady()).isEqualTo(1)
verify(mediaController).unregisterCallback(anyObject())
- assertThat(executor.numPending()).isEqualTo(0)
+ assertThat(mainExecutor.numPending()).isEqualTo(0)
verify(logger).logSessionDestroyed(eq(KEY))
verify(sessionCallback).invoke(eq(KEY))
}
@@ -306,11 +314,11 @@
whenever(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
whenever(mediaController.playbackState).thenReturn(playingState)
val mediaPlaying = mediaData.copy(isPlaying = true)
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaPlaying)
+ loadMediaData(KEY, null, mediaPlaying)
// THEN the timeout runnable will update the state
- assertThat(executor.numPending()).isEqualTo(1)
- with(executor) {
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
+ with(mainExecutor) {
advanceClockToNext()
runAllReady()
}
@@ -322,31 +330,32 @@
fun testOnSessionDestroyed_resume_continuesTimeout() {
// GIVEN resume media with session info
val resumeWithSession = resumeData.copy(token = session.sessionToken)
- mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeWithSession)
+ loadMediaData(PACKAGE, null, resumeWithSession)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
// WHEN the session is destroyed
mediaCallbackCaptor.value.onSessionDestroyed()
// THEN the controller is unregistered, but the timeout is still scheduled
+ assertThat(bgExecutor.runAllReady()).isEqualTo(1)
verify(mediaController).unregisterCallback(anyObject())
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
verify(sessionCallback, never()).invoke(eq(KEY))
}
@Test
fun testOnMediaDataLoaded_activeToResume_registersTimeout() {
// WHEN a regular media is loaded
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ loadMediaData(KEY, null, mediaData)
// AND it turns into a resume control
- mediaTimeoutListener.onMediaDataLoaded(PACKAGE, KEY, resumeData)
+ loadMediaData(PACKAGE, KEY, resumeData)
// THEN we register a timeout
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
- assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
+ assertThat(mainExecutor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
}
@Test
@@ -355,42 +364,42 @@
val pausedState =
PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()
whenever(mediaController.playbackState).thenReturn(pausedState)
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
- assertThat(executor.numPending()).isEqualTo(1)
+ loadMediaData(KEY, null, mediaData)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
// AND it turns into a resume control
- mediaTimeoutListener.onMediaDataLoaded(PACKAGE, KEY, resumeData)
+ loadMediaData(PACKAGE, KEY, resumeData)
// THEN we update the timeout length
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
- assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
+ assertThat(mainExecutor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
}
@Test
fun testOnMediaDataLoaded_resumption_registersTimeout() {
// WHEN a resume media is loaded
- mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeData)
+ loadMediaData(PACKAGE, null, resumeData)
// THEN we register a timeout
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
- assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
+ assertThat(mainExecutor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT)
}
@Test
fun testOnMediaDataLoaded_resumeToActive_updatesTimeout() {
// WHEN we have a resume control
- mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeData)
+ loadMediaData(PACKAGE, null, resumeData)
// AND that media is resumed
val playingState =
PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()
whenever(mediaController.playbackState).thenReturn(playingState)
- mediaTimeoutListener.onMediaDataLoaded(KEY, PACKAGE, mediaData)
+ loadMediaData(oldKey = PACKAGE, data = mediaData)
// THEN the timeout length is changed to a regular media control
- assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
+ assertThat(mainExecutor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT)
}
@Test
@@ -401,7 +410,7 @@
mediaTimeoutListener.onMediaDataRemoved(PACKAGE, false)
// THEN the timeout runnable is cancelled
- assertThat(executor.numPending()).isEqualTo(0)
+ assertThat(mainExecutor.numPending()).isEqualTo(0)
}
@Test
@@ -427,6 +436,7 @@
// When the playback state changes, and has different actions
val playingState = PlaybackState.Builder().setActions(PlaybackState.ACTION_PLAY).build()
mediaCallbackCaptor.value.onPlaybackStateChanged(playingState)
+ assertThat(uiExecutor.runAllReady()).isEqualTo(1)
// Then the callback is invoked
verify(stateCallback).invoke(eq(KEY), eq(playingState!!))
@@ -463,6 +473,7 @@
.addCustomAction(customTwo)
.build()
mediaCallbackCaptor.value.onPlaybackStateChanged(pausedStateTwoActions)
+ assertThat(uiExecutor.runAllReady()).isEqualTo(1)
// Then the callback is invoked
verify(stateCallback).invoke(eq(KEY), eq(pausedStateTwoActions!!))
@@ -534,6 +545,7 @@
val playingState =
PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
mediaCallbackCaptor.value.onPlaybackStateChanged(playingState)
+ uiExecutor.runAllReady()
// Then the callback is invoked
verify(stateCallback).invoke(eq(KEY), eq(playingState!!))
@@ -567,7 +579,7 @@
// And we doze past the scheduled timeout
val time = clock.currentTimeMillis()
clock.setElapsedRealtime(time + PAUSED_MEDIA_TIMEOUT)
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
// Then when no longer dozing, the timeout runs immediately
dozingCallbackCaptor.value.onDozingChanged(false)
@@ -576,7 +588,7 @@
// and cancel any later scheduled timeout
verify(logger).logTimeoutCancelled(eq(KEY), any())
- assertThat(executor.numPending()).isEqualTo(0)
+ assertThat(mainExecutor.numPending()).isEqualTo(0)
}
@Test
@@ -592,12 +604,12 @@
// And we doze, but not past the scheduled timeout
clock.setElapsedRealtime(time + PAUSED_MEDIA_TIMEOUT / 2L)
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
// Then when no longer dozing, the timeout remains scheduled
dozingCallbackCaptor.value.onDozingChanged(false)
verify(timeoutCallback, never()).invoke(eq(KEY), eq(true))
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
}
@Test
@@ -610,8 +622,8 @@
whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- assertThat(executor.numPending()).isEqualTo(1)
- assertThat(executor.advanceClockToNext()).isEqualTo(duration)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.advanceClockToNext()).isEqualTo(duration)
}
@Test
@@ -619,7 +631,7 @@
// Given a pending timeout
testSmartspaceDataLoaded_schedulesTimeout()
- executor.runAllReady()
+ mainExecutor.runAllReady()
verify(timeoutCallback).invoke(eq(SMARTSPACE_KEY), eq(true))
}
@@ -634,14 +646,14 @@
whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
val expiryLonger = expireTime + duration
whenever(smartspaceData.expiryTimeMs).thenReturn(expiryLonger)
mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- assertThat(executor.numPending()).isEqualTo(1)
- assertThat(executor.advanceClockToNext()).isEqualTo(duration * 2)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.advanceClockToNext()).isEqualTo(duration * 2)
}
@Test
@@ -649,10 +661,10 @@
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
mediaTimeoutListener.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
- assertThat(executor.numPending()).isEqualTo(0)
+ assertThat(mainExecutor.numPending()).isEqualTo(0)
}
@Test
@@ -667,12 +679,12 @@
whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
// And we doze past the scheduled timeout
val time = clock.currentTimeMillis()
clock.setElapsedRealtime(time + duration * 2)
- assertThat(executor.numPending()).isEqualTo(1)
+ assertThat(mainExecutor.numPending()).isEqualTo(1)
// Then when no longer dozing, the timeout runs immediately
dozingCallbackCaptor.value.onDozingChanged(false)
@@ -680,12 +692,18 @@
verify(logger).logTimeout(eq(SMARTSPACE_KEY))
// and cancel any later scheduled timeout
- assertThat(executor.numPending()).isEqualTo(0)
+ assertThat(mainExecutor.numPending()).isEqualTo(0)
}
private fun loadMediaDataWithPlaybackState(state: PlaybackState) {
whenever(mediaController.playbackState).thenReturn(state)
- mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ loadMediaData(data = mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
}
+
+ private fun loadMediaData(key: String = KEY, oldKey: String? = null, data: MediaData) {
+ mediaTimeoutListener.onMediaDataLoaded(key, oldKey, data)
+ bgExecutor.runAllReady()
+ uiExecutor.runAllReady()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
index 02d7413..bc29d2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
@@ -138,6 +138,7 @@
whenever(mockContext.packageManager).thenReturn(context.packageManager)
whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
whenever(mockContext.userId).thenReturn(context.userId)
+ whenever(mockContext.resources).thenReturn(context.resources)
whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false)
executor = FakeExecutor(clock)
@@ -210,7 +211,7 @@
@Test
fun testOnLoad_checksForResume_noService() {
// When media data is loaded that has not been checked yet, and does not have a MBS
- resumeListener.onMediaDataLoaded(KEY, null, data)
+ onMediaDataLoaded(KEY, null, data)
// Then we report back to the manager
verify(mediaDataManager).setResumeAction(KEY, null)
@@ -223,8 +224,7 @@
whenever(resumeBrowser.testConnection()).thenAnswer { callbackCaptor.value.onError() }
// When media data is loaded that has not been checked yet, and does not have a MBS
- resumeListener.onMediaDataLoaded(KEY, null, data)
- executor.runAllReady()
+ onMediaDataLoaded(KEY, null, data)
// Then we report back to the manager
verify(mediaDataManager).setResumeAction(eq(KEY), eq(null))
@@ -234,7 +234,7 @@
fun testOnLoad_localCast_doesNotCheck() {
// When media data is loaded that has not been checked yet, and is a local cast
val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
- resumeListener.onMediaDataLoaded(KEY, null, dataCast)
+ onMediaDataLoaded(KEY, null, dataCast, false)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
@@ -244,7 +244,7 @@
fun testOnload_remoteCast_doesNotCheck() {
// When media data is loaded that has not been checked yet, and is a remote cast
val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE)
- resumeListener.onMediaDataLoaded(KEY, null, dataRcn)
+ onMediaDataLoaded(KEY, null, dataRcn, resume = false)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
@@ -257,7 +257,7 @@
// When media data is loaded that has not been checked yet, and is a local cast
val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
- resumeListener.onMediaDataLoaded(KEY, null, dataCast)
+ onMediaDataLoaded(KEY, null, dataCast)
// Then we report back to the manager
verify(mediaDataManager).setResumeAction(KEY, null)
@@ -270,7 +270,7 @@
// When media data is loaded that has not been checked yet, and is a remote cast
val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE)
- resumeListener.onMediaDataLoaded(KEY, null, dataRcn)
+ onMediaDataLoaded(KEY, null, dataRcn, false)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
@@ -288,10 +288,9 @@
// When media data is loaded that has not been checked yet, and does have a MBS
val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ onMediaDataLoaded(KEY, null, dataCopy)
// Then we test whether the service is valid
- executor.runAllReady()
verify(mediaDataManager).setResumeAction(eq(KEY), eq(null))
verify(resumeBrowser).testConnection()
@@ -307,7 +306,7 @@
fun testOnLoad_doesNotCheckAgain() {
// When a media data is loaded that has been checked already
var dataCopy = data.copy(hasCheckedForResume = true)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ onMediaDataLoaded(KEY, null, dataCopy, resume = false)
// Then we should not check it again
verify(resumeBrowser, never()).testConnection()
@@ -320,17 +319,15 @@
setUpMbsWithValidResolveInfo()
resumeListener.onMediaDataLoaded(KEY, null, data)
- // We notify the manager to set a null action
- verify(mediaDataManager).setResumeAction(KEY, null)
-
// If we then get another update from the app before the first check completes
assertThat(executor.numPending()).isEqualTo(1)
var dataWithCheck = data.copy(hasCheckedForResume = true)
resumeListener.onMediaDataLoaded(KEY, null, dataWithCheck)
// We do not try to start another check
- assertThat(executor.numPending()).isEqualTo(1)
+ executor.runAllReady()
verify(mediaDataManager).setResumeAction(KEY, null)
+ verify(resumeBrowserFactory, times(1)).create(any(), any(), anyInt())
}
@Test
@@ -363,6 +360,7 @@
resumeListener.userUnlockReceiver.onReceive(context, intent)
// Then we should attempt to find recent media for each saved component
+ executor.runAllReady()
verify(resumeBrowser, times(3)).findRecentMedia()
// Then since the mock service found media, the manager should be informed
@@ -382,10 +380,9 @@
// When media data is loaded that has not been checked yet, and does have a MBS
val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ onMediaDataLoaded(KEY, null, dataCopy)
// Then we test whether the service is valid and set the resume action
- executor.runAllReady()
verify(mediaDataManager).setResumeAction(eq(KEY), eq(null))
verify(resumeBrowser).testConnection()
verify(mediaDataManager, times(2)).setResumeAction(eq(KEY), capture(actionCaptor))
@@ -455,6 +452,7 @@
resumeListener.userUnlockReceiver.onReceive(mockContext, intent)
// We add its resume controls
+ executor.runAllReady()
verify(resumeBrowser).findRecentMedia()
verify(mediaDataManager)
.addResumptionControls(anyInt(), any(), any(), any(), any(), any(), eq(PACKAGE_NAME))
@@ -527,7 +525,7 @@
// When media data is loaded that has not been checked yet, and does have a MBS
val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ onMediaDataLoaded(KEY, null, dataCopy)
// Then we store the new lastPlayed time
verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
@@ -546,10 +544,9 @@
fun testOnMediaDataLoaded_newKeyDifferent_oldMediaBrowserDisconnected() {
setUpMbsWithValidResolveInfo()
- resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
- executor.runAllReady()
+ onMediaDataLoaded(key = KEY, oldKey = null, data)
- resumeListener.onMediaDataLoaded(key = "newKey", oldKey = KEY, data)
+ onMediaDataLoaded(key = "newKey", oldKey = KEY, data)
verify(resumeBrowser).disconnect()
}
@@ -561,8 +558,7 @@
// Set up mocks to return with an error
whenever(resumeBrowser.testConnection()).thenAnswer { callbackCaptor.value.onError() }
- resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
- executor.runAllReady()
+ onMediaDataLoaded(key = KEY, oldKey = null, data)
// Ensure we disconnect the browser
verify(resumeBrowser).disconnect()
@@ -579,8 +575,7 @@
callbackCaptor.value.addTrack(description, component, resumeBrowser)
}
- resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
- executor.runAllReady()
+ onMediaDataLoaded(key = KEY, oldKey = null, data)
// Ensure we disconnect the browser
verify(resumeBrowser).disconnect()
@@ -598,8 +593,7 @@
// Load media data that will require us to get the resume action
val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
- executor.runAllReady()
+ onMediaDataLoaded(KEY, null, dataCopy)
verify(mediaDataManager, times(2)).setResumeAction(eq(KEY), capture(actionCaptor))
// Set up our factory to return a new browser so we can verify we disconnected the old one
@@ -634,6 +628,7 @@
// When the first user unlocks and we query their recent media
userCallbackCaptor.value.onUserChanged(firstUserId, context)
resumeListener.userUnlockReceiver.onReceive(context, unlockIntent)
+ executor.runAllReady()
whenever(resumeBrowser.userId).thenReturn(userIdCaptor.value)
verify(resumeBrowser, times(3)).findRecentMedia()
@@ -688,4 +683,16 @@
whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(resolveInfo)
whenever(pm.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
}
+
+ private fun onMediaDataLoaded(
+ key: String,
+ oldKey: String?,
+ data: MediaData,
+ resume: Boolean = true
+ ) {
+ resumeListener.onMediaDataLoaded(key, oldKey, data)
+ if (resume) {
+ assertThat(executor.runAllReady()).isEqualTo(1)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 46c66e0..03667cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -48,6 +48,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.unconfinedTestDispatcher
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -72,9 +73,8 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Locale
@@ -84,9 +84,7 @@
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -120,7 +118,9 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
class MediaCarouselControllerTest : SysuiTestCase() {
- val kosmos = testKosmos()
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.unconfinedTestDispatcher
+ private val secureSettings = kosmos.unconfinedDispatcherFakeSettings
@Mock lateinit var mediaControlPanelFactory: Provider<MediaControlPanel>
@Mock lateinit var mediaViewControllerFactory: Provider<MediaViewController>
@@ -142,7 +142,6 @@
@Mock lateinit var mediaFlags: MediaFlags
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock lateinit var globalSettings: GlobalSettings
- private lateinit var secureSettings: SecureSettings
private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
@Captor
@@ -154,7 +153,6 @@
private val clock = FakeSystemClock()
private lateinit var bgExecutor: FakeExecutor
- private lateinit var testDispatcher: TestDispatcher
private lateinit var mediaCarouselController: MediaCarouselController
private var originalResumeSetting =
@@ -163,10 +161,8 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- secureSettings = FakeSettings()
context.resources.configuration.setLocales(LocaleList(Locale.US, Locale.UK))
bgExecutor = FakeExecutor(clock)
- testDispatcher = UnconfinedTestDispatcher()
mediaCarouselController =
MediaCarouselController(
applicationScope = kosmos.applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 1260a65..68a5d93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -79,6 +79,7 @@
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.ui.binder.SeekBarObserver
import com.android.systemui.media.controls.ui.view.GutsViewHolder
@@ -236,6 +237,19 @@
@Mock private lateinit var recProgressBar3: SeekBar
@Mock private lateinit var globalSettings: GlobalSettings
+ private val intent =
+ Intent().apply {
+ putExtras(Bundle().also { it.putString(KEY_SMARTSPACE_APP_NAME, REC_APP_NAME) })
+ setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ private val pendingIntent =
+ PendingIntent.getActivity(
+ mContext,
+ 0,
+ intent.setPackage(mContext.packageName),
+ PendingIntent.FLAG_MUTABLE
+ )
+
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Before
@@ -989,14 +1003,13 @@
@Test
fun bindNotificationActions() {
val icon = context.getDrawable(android.R.drawable.ic_media_play)
- val bg = context.getDrawable(R.drawable.qs_media_round_button_background)
val actions =
listOf(
- MediaAction(icon, Runnable {}, "previous", bg),
- MediaAction(icon, Runnable {}, "play", bg),
- MediaAction(icon, null, "next", bg),
- MediaAction(icon, null, "custom 0", bg),
- MediaAction(icon, Runnable {}, "custom 1", bg)
+ MediaNotificationAction(true, actionIntent = pendingIntent, icon, "previous"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, icon, "play"),
+ MediaNotificationAction(true, actionIntent = null, icon, "next"),
+ MediaNotificationAction(true, actionIntent = null, icon, "custom 0"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, icon, "custom 1")
)
val state =
mediaData.copy(
@@ -1684,11 +1697,11 @@
fun actionCustom2Click_isLogged() {
val actions =
listOf(
- MediaAction(null, Runnable {}, "action 0", null),
- MediaAction(null, Runnable {}, "action 1", null),
- MediaAction(null, Runnable {}, "action 2", null),
- MediaAction(null, Runnable {}, "action 3", null),
- MediaAction(null, Runnable {}, "action 4", null)
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 0"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
)
val data = mediaData.copy(actions = actions)
@@ -1703,11 +1716,11 @@
fun actionCustom3Click_isLogged() {
val actions =
listOf(
- MediaAction(null, Runnable {}, "action 0", null),
- MediaAction(null, Runnable {}, "action 1", null),
- MediaAction(null, Runnable {}, "action 2", null),
- MediaAction(null, Runnable {}, "action 3", null),
- MediaAction(null, Runnable {}, "action 4", null)
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 0"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
)
val data = mediaData.copy(actions = actions)
@@ -1722,11 +1735,11 @@
fun actionCustom4Click_isLogged() {
val actions =
listOf(
- MediaAction(null, Runnable {}, "action 0", null),
- MediaAction(null, Runnable {}, "action 1", null),
- MediaAction(null, Runnable {}, "action 2", null),
- MediaAction(null, Runnable {}, "action 3", null),
- MediaAction(null, Runnable {}, "action 4", null)
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 0"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 1"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 2"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 3"),
+ MediaNotificationAction(true, actionIntent = pendingIntent, null, "action 4")
)
val data = mediaData.copy(actions = actions)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index c1cf91d..bc0ec2d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -22,6 +22,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+import static com.android.systemui.Flags.FLAG_QS_QUICK_REBIND_ACTIVE_TILES;
import static com.google.common.truth.Truth.assertThat;
@@ -75,6 +76,8 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.google.common.truth.Truth;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -95,7 +98,8 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX,
+ FLAG_QS_QUICK_REBIND_ACTIVE_TILES);
}
private final PackageManagerAdapter mMockPackageManagerAdapter =
@@ -154,7 +158,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
}
@After
@@ -169,12 +174,12 @@
mStateManager.handleDestroy();
}
- private void setPackageEnabled(boolean enabled) throws Exception {
+ private void setPackageEnabledAndActive(boolean enabled, boolean active) throws Exception {
ServiceInfo defaultServiceInfo = null;
if (enabled) {
defaultServiceInfo = new ServiceInfo();
defaultServiceInfo.metaData = new Bundle();
- defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true);
+ defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, active);
defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_TOGGLEABLE_TILE, true);
}
when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt()))
@@ -186,6 +191,10 @@
.thenReturn(defaultPackageInfo);
}
+ private void setPackageEnabled(boolean enabled) throws Exception {
+ setPackageEnabledAndActive(enabled, true);
+ }
+
private void setPackageInstalledForUser(
boolean installed,
boolean active,
@@ -396,13 +405,40 @@
}
@Test
- public void testKillProcess() throws Exception {
+ public void testKillProcessWhenTileServiceIsNotActive() throws Exception {
+ setPackageEnabledAndActive(true, false);
mStateManager.onStartListening();
mStateManager.executeSetBindService(true);
mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
mStateManager.onBindingDied(mTileServiceComponentName);
mExecutor.runAllReady();
- mClock.advanceTime(5000);
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+
+ // still 4 seconds left because non active tile service rebind time is 5 seconds
+ Truth.assertThat(mContext.isBound(mTileServiceComponentName)).isFalse();
+
+ mClock.advanceTime(4000); // 5 seconds delay for nonActive service rebinding
+ mExecutor.runAllReady();
+ verifyBind(2);
+ verify(mMockTileService, times(2)).onStartListening();
+ }
+
+ @EnableFlags(FLAG_QS_QUICK_REBIND_ACTIVE_TILES)
+ @Test
+ public void testKillProcessWhenTileServiceIsActive_withRebindFlagOn() throws Exception {
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
mExecutor.runAllReady();
// Two calls: one for the first bind, one for the restart.
@@ -410,6 +446,86 @@
verify(mMockTileService, times(2)).onStartListening();
}
+ @DisableFlags(FLAG_QS_QUICK_REBIND_ACTIVE_TILES)
+ @Test
+ public void testKillProcessWhenTileServiceIsActive_withRebindFlagOff() throws Exception {
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+ verifyBind(0); // the rebind happens after 4 more seconds
+
+ mClock.advanceTime(4000);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ }
+
+ @EnableFlags(FLAG_QS_QUICK_REBIND_ACTIVE_TILES)
+ @Test
+ public void testKillProcessWhenTileServiceIsActiveTwice_withRebindFlagOn_delaysSecondRebind()
+ throws Exception {
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+
+ // Two calls: one for the first bind, one for the restart.
+ verifyBind(2);
+ verify(mMockTileService, times(2)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+ // because active tile will take 5 seconds to bind the second time, not 1
+ verifyBind(0);
+
+ mClock.advanceTime(4000);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ }
+
+ @DisableFlags(FLAG_QS_QUICK_REBIND_ACTIVE_TILES)
+ @Test
+ public void testKillProcessWhenTileServiceIsActiveTwice_withRebindFlagOff_rebindsFromFirstKill()
+ throws Exception {
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName); // rebind scheduled for 5 seconds
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+
+ verifyBind(0); // it would bind in 4 more seconds
+
+ mStateManager.onBindingDied(mTileServiceComponentName); // this does not affect the rebind
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+
+ verifyBind(0); // only 2 seconds passed from first kill
+
+ mClock.advanceTime(3000);
+ mExecutor.runAllReady();
+ verifyBind(1); // the rebind scheduled 5 seconds from the first kill should now happen
+ }
+
@Test
public void testKillProcessLowMemory() throws Exception {
doAnswer(invocation -> {
@@ -510,7 +626,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
manager.executeSetBindService(true);
mExecutor.runAllReady();
@@ -533,7 +650,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
manager.executeSetBindService(true);
mExecutor.runAllReady();
@@ -556,7 +674,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
manager.executeSetBindService(true);
mExecutor.runAllReady();
@@ -581,7 +700,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
manager.executeSetBindService(true);
mExecutor.runAllReady();
@@ -607,7 +727,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isActiveTile()).isTrue();
}
@@ -626,7 +747,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isActiveTile()).isTrue();
}
@@ -644,7 +766,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isToggleableTile()).isTrue();
}
@@ -663,7 +786,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isToggleableTile()).isTrue();
}
@@ -682,7 +806,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isToggleableTile()).isFalse();
assertThat(manager.isActiveTile()).isFalse();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index d9faa30..70af5e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -31,17 +31,18 @@
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.Text
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -199,10 +200,11 @@
android.R.drawable.star_on,
ContentDescription.Loaded(tileSpec)
),
- label = Text.Loaded(tileSpec),
+ label = AnnotatedString(tileSpec),
appName = null,
isCurrent = true,
availableEditActions = emptySet(),
+ category = TileCategory.UNKNOWN,
),
getWidth(tileSpec),
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
index 76c8cf0..7d41a20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.LAUNCH_SOURCE_QS_TILE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -151,7 +153,7 @@
mTile.handleClick(expandable);
mTestableLooper.processAllMessages();
- verify(mHearingDevicesDialogManager).showDialog(expandable);
+ verify(mHearingDevicesDialogManager).showDialog(expandable, LAUNCH_SOURCE_QS_TILE);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index 828c7b2..9f84e34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -258,8 +258,7 @@
companion object {
const val WIFI_SSID = "test ssid"
val ACTIVE_WIFI =
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 4,
ssid = WIFI_SSID,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index 19735e2..848c8db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -25,10 +25,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.settingslib.notification.data.repository.FakeZenModeRepository
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
@@ -36,21 +39,21 @@
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.res.R
-import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
-import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -59,7 +62,6 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@@ -68,6 +70,9 @@
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class ModesTileTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val testDispatcher = kosmos.testDispatcher
@Mock private lateinit var qsHost: QSHost
@@ -85,25 +90,13 @@
@Mock private lateinit var dialogDelegate: ModesDialogDelegate
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
-
private val inputHandler = FakeQSTileIntentUserInputHandler()
- private val zenModeRepository = FakeZenModeRepository()
+ private val zenModeRepository = kosmos.zenModeRepository
private val tileDataInteractor =
- ModesTileDataInteractor(
- context,
- ZenModeInteractor(context, zenModeRepository, mock<NotificationSettingsRepository>()),
- testDispatcher
- )
+ ModesTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher)
private val mapper =
ModesTileMapper(
- context.orCreateTestableResources
- .apply {
- addOverride(R.drawable.qs_dnd_icon_on, TestStubDrawable())
- addOverride(R.drawable.qs_dnd_icon_off, TestStubDrawable())
- }
- .resources,
+ context.resources,
context.theme,
)
@@ -127,7 +120,7 @@
QSTileConfigTestBuilder.build {
uiConfig =
QSTileUIConfig.Resource(
- iconRes = R.drawable.qs_dnd_icon_off,
+ iconRes = ModesTile.ICON_RES_ID,
labelRes = R.string.quick_settings_modes_label,
)
}
@@ -179,4 +172,43 @@
assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
}
+
+ @Test
+ fun handleUpdateState_withTileModel_updatesState() =
+ testScope.runTest {
+ val tileState =
+ QSTile.State().apply {
+ state = Tile.STATE_INACTIVE
+ secondaryLabel = "Old secondary label"
+ }
+ val model =
+ ModesTileModel(
+ isActivated = true,
+ activeModes = listOf("One", "Two"),
+ icon = TestStubDrawable().asIcon()
+ )
+
+ underTest.handleUpdateState(tileState, model)
+
+ assertThat(tileState.state).isEqualTo(Tile.STATE_ACTIVE)
+ assertThat(tileState.secondaryLabel).isEqualTo("2 modes are active")
+ }
+
+ @Test
+ fun handleUpdateState_withNull_updatesState() =
+ testScope.runTest {
+ val tileState =
+ QSTile.State().apply {
+ state = Tile.STATE_INACTIVE
+ secondaryLabel = "Old secondary label"
+ }
+ zenModeRepository.addMode("One", active = true)
+ zenModeRepository.addMode("Two", active = true)
+ runCurrent()
+
+ underTest.handleUpdateState(tileState, null)
+
+ assertThat(tileState.state).isEqualTo(Tile.STATE_ACTIVE)
+ assertThat(tileState.secondaryLabel).isEqualTo("2 modes are active")
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index 73548ba..ca518f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.recordissue.TraceurMessageSender
import com.android.systemui.res.R
+import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -65,6 +66,7 @@
@Mock private lateinit var qsEventLogger: QsEventLogger
@Mock private lateinit var metricsLogger: MetricsLogger
@Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var recordingController: RecordingController
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil
@@ -109,6 +111,7 @@
Executors.newSingleThreadExecutor(),
issueRecordingState,
delegateFactory,
+ recordingController,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index d6bde27..1aff45b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -18,6 +18,8 @@
import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -25,6 +27,7 @@
import android.os.Handler;
import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
@@ -35,6 +38,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.server.display.feature.flags.Flags;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.extradim.ExtraDimDialogManager;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
@@ -74,6 +78,8 @@
private ReduceBrightColorsController mReduceBrightColorsController;
@Mock
private QsEventLogger mUiEventLogger;
+ @Mock
+ private ExtraDimDialogManager mExtraDimDialogManager;
private TestableLooper mTestableLooper;
private ReduceBrightColorsTile mTile;
@@ -97,7 +103,8 @@
mMetricsLogger,
mStatusBarStateController,
mActivityStarter,
- mQSLogger
+ mQSLogger,
+ mExtraDimDialogManager
);
mTile.initialize();
@@ -148,6 +155,78 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_EVEN_DIMMER)
+ public void testDialogueShownOnClick() {
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(true);
+ when(mReduceBrightColorsController.isInUpgradeMode(mContext.getResources()))
+ .thenReturn(true);
+ mTile = new ReduceBrightColorsTile(
+ true,
+ mReduceBrightColorsController,
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mExtraDimDialogManager
+ );
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
+
+ // Validity check
+ assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
+ mTile.handleClick(null /* view */);
+
+ verify(mExtraDimDialogManager, times(1))
+ .dismissKeyguardIfNeededAndShowDialog(any());
+ verify(mReduceBrightColorsController, times(0))
+ .setReduceBrightColorsActivated(anyBoolean());
+ mTile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_EVEN_DIMMER)
+ public void testDialogueShownOnLongClick() {
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(true);
+ when(mReduceBrightColorsController.isInUpgradeMode(mContext.getResources()))
+ .thenReturn(true);
+ mTile = new ReduceBrightColorsTile(
+ true,
+ mReduceBrightColorsController,
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mExtraDimDialogManager
+ );
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
+
+ // Validity check
+ assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
+ mTile.handleLongClick(null /* view */);
+
+ verify(mExtraDimDialogManager, times(1))
+ .dismissKeyguardIfNeededAndShowDialog(any());
+ verify(mReduceBrightColorsController, times(0))
+ .setReduceBrightColorsActivated(anyBoolean());
+ mTile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
public void testIcon_whenTileEnabled_isOnState() {
when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(true);
mTile.refreshState();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
new file mode 100644
index 0000000..d8618fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.recordissue
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
+import com.android.traceur.PresetTraceConfigs
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+/**
+ * CustomTraceState is customized Traceur settings for power users. These settings determine what
+ * tracing is used during the Record Issue Quick Settings flow. This class tests that those features
+ * are persistently and accurately stored across sessions.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected](setAsMainLooper = true)
+class CustomTraceStateTest : SysuiTestCase() {
+
+ private lateinit var underTest: CustomTraceState
+
+ @Before
+ fun setup() {
+ underTest = CustomTraceState(context.getSharedPreferences(TILE_SPEC, 0))
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun trace_config_is_stored_accurately() {
+ val expected = PresetTraceConfigs.getUiConfig()
+
+ underTest.traceConfig = expected
+
+ val actual = underTest.traceConfig
+ Truth.assertThat(actual.longTrace).isEqualTo(expected.longTrace)
+ Truth.assertThat(actual.maxLongTraceSizeMb).isEqualTo(expected.maxLongTraceSizeMb)
+ Truth.assertThat(actual.maxLongTraceDurationMinutes)
+ .isEqualTo(expected.maxLongTraceDurationMinutes)
+ Truth.assertThat(actual.apps).isEqualTo(expected.apps)
+ Truth.assertThat(actual.winscope).isEqualTo(expected.winscope)
+ Truth.assertThat(actual.attachToBugreport).isEqualTo(expected.attachToBugreport)
+ Truth.assertThat(actual.bufferSizeKb).isEqualTo(expected.bufferSizeKb)
+ Truth.assertThat(actual.tags).isEqualTo(expected.tags)
+ }
+
+ @Test
+ fun trace_config_is_persistently_stored_between_instances() {
+ val expected = PresetTraceConfigs.getUiConfig()
+
+ underTest.traceConfig = expected
+
+ val actual = CustomTraceState(context.getSharedPreferences(TILE_SPEC, 0)).traceConfig
+ Truth.assertThat(actual.longTrace).isEqualTo(expected.longTrace)
+ Truth.assertThat(actual.maxLongTraceSizeMb).isEqualTo(expected.maxLongTraceSizeMb)
+ Truth.assertThat(actual.maxLongTraceDurationMinutes)
+ .isEqualTo(expected.maxLongTraceDurationMinutes)
+ Truth.assertThat(actual.apps).isEqualTo(expected.apps)
+ Truth.assertThat(actual.winscope).isEqualTo(expected.winscope)
+ Truth.assertThat(actual.attachToBugreport).isEqualTo(expected.attachToBugreport)
+ Truth.assertThat(actual.bufferSizeKb).isEqualTo(expected.bufferSizeKb)
+ Truth.assertThat(actual.tags).isEqualTo(expected.tags)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt
new file mode 100644
index 0000000..57cfe1b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingServiceCommandHandlerTest.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.recordissue
+
+import android.app.IActivityManager
+import android.app.NotificationManager
+import android.net.Uri
+import android.os.UserHandle
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.settings.userTracker
+import com.android.traceur.TraceConfig
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected](setAsMainLooper = true)
+class IssueRecordingServiceCommandHandlerTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos().also { it.testCase = this }
+ private val bgExecutor = kosmos.fakeExecutor
+ private val userContextProvider: UserContextProvider = kosmos.userTracker
+ private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
+ private lateinit var traceurMessageSender: TraceurMessageSender
+ private val issueRecordingState =
+ IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ private val iActivityManager = mock<IActivityManager>()
+ private val notificationManager = mock<NotificationManager>()
+ private val panelInteractor = mock<PanelInteractor>()
+
+ private lateinit var underTest: IssueRecordingServiceCommandHandler
+
+ @Before
+ fun setup() {
+ traceurMessageSender = mock<TraceurMessageSender>()
+ underTest =
+ IssueRecordingServiceCommandHandler(
+ bgExecutor,
+ dialogTransitionAnimator,
+ panelInteractor,
+ traceurMessageSender,
+ issueRecordingState,
+ iActivityManager,
+ notificationManager,
+ userContextProvider
+ )
+ }
+
+ @Test
+ fun startsTracing_afterReceivingActionStartCommand() {
+ underTest.handleStartCommand()
+ bgExecutor.runAllReady()
+
+ Truth.assertThat(issueRecordingState.isRecording).isTrue()
+ verify(traceurMessageSender).startTracing(any<TraceConfig>())
+ }
+
+ @Test
+ fun stopsTracing_afterReceivingStopTracingCommand() {
+ underTest.handleStopCommand(mContext.contentResolver)
+ bgExecutor.runAllReady()
+
+ Truth.assertThat(issueRecordingState.isRecording).isFalse()
+ verify(traceurMessageSender).stopTracing()
+ }
+
+ @Test
+ fun cancelsNotification_afterReceivingShareCommand() {
+ underTest.handleShareCommand(0, null, mContext)
+ bgExecutor.runAllReady()
+
+ verify(notificationManager).cancelAsUser(isNull(), anyInt(), any<UserHandle>())
+ }
+
+ @Test
+ fun requestBugreport_afterReceivingShareCommand_withTakeBugreportTrue() {
+ issueRecordingState.takeBugreport = true
+ val uri = mock<Uri>()
+
+ underTest.handleShareCommand(0, uri, mContext)
+ bgExecutor.runAllReady()
+
+ verify(iActivityManager).requestBugReportWithExtraAttachment(uri)
+ }
+
+ @Test
+ fun sharesTracesDirectly_afterReceivingShareCommand_withTakeBugreportFalse() {
+ issueRecordingState.takeBugreport = false
+ val uri = mock<Uri>()
+
+ underTest.handleShareCommand(0, uri, mContext)
+ bgExecutor.runAllReady()
+
+ verify(traceurMessageSender).shareTraces(mContext, uri)
+ }
+
+ @Test
+ fun closesShade_afterReceivingShareCommand() {
+ val uri = mock<Uri>()
+
+ underTest.handleShareCommand(0, uri, mContext)
+ bgExecutor.runAllReady()
+
+ verify(panelInteractor).collapsePanels()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
new file mode 100644
index 0000000..4ab3c7b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.recordissue
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth
+import java.util.concurrent.CountDownLatch
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected](setAsMainLooper = true)
+class IssueRecordingStateTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos()
+ private lateinit var underTest: IssueRecordingState
+
+ @Before
+ fun setup() {
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ }
+
+ @Test
+ fun takeBugreport_isSaved_betweenDifferentSessions() {
+ val expected = true
+
+ underTest.takeBugreport = expected
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ Truth.assertThat(underTest.takeBugreport).isEqualTo(expected)
+ }
+
+ @Test
+ fun recordScreen_isSaved_betweenDifferentSessions() {
+ val expected = true
+
+ underTest.recordScreen = expected
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ Truth.assertThat(underTest.recordScreen).isEqualTo(expected)
+ }
+
+ @Test
+ fun hasUserApprovedScreenRecording_isTrue_afterBeingMarkedAsCompleted() {
+ underTest.markUserApprovalForScreenRecording()
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ Truth.assertThat(underTest.hasUserApprovedScreenRecording).isEqualTo(true)
+ }
+
+ @Test
+ fun tagTitles_areSavedConsistently() {
+ val expected = setOf("a", "b", "c")
+
+ underTest.tagTitles = expected
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ Truth.assertThat(underTest.tagTitles).isEqualTo(expected)
+ }
+
+ @Test
+ fun isRecording_callsListeners_onTheValueChanging() {
+ val count = CountDownLatch(1)
+ val listener = Runnable { count.countDown() }
+
+ underTest.addListener(listener)
+ underTest.isRecording = true
+
+ Truth.assertThat(count.count).isEqualTo(0)
+ }
+
+ @Test
+ fun isRecording_callsOnlyListeners_whoHaveNotBeenRemoved() {
+ val count1 = CountDownLatch(1)
+ val count2 = CountDownLatch(1)
+ val listener1 = Runnable { count1.countDown() }
+ val listener2 = Runnable { count2.countDown() }
+
+ underTest.addListener(listener1)
+ underTest.removeListener(listener1)
+ underTest.addListener(listener2)
+ underTest.isRecording = true
+
+ Truth.assertThat(count1.count).isEqualTo(1)
+ Truth.assertThat(count2.count).isEqualTo(0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
index b31d21e..15da77d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -2,8 +2,6 @@
import android.graphics.drawable.Drawable
import android.os.UserHandle
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
@@ -90,20 +88,6 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_SCREENSHOT_PRIVATE_PROFILE_BEHAVIOR_FIX)
- fun testOnScreenshotTakenUserHandle_withWorkProfileFirstRun() {
- whenever(workProfileMessageController.onScreenshotTaken(eq(userHandle)))
- .thenReturn(workProfileData)
- messageContainer.onScreenshotTaken(screenshotData)
-
- verify(workProfileMessageController)
- .populateView(eq(workProfileFirstRunView), eq(workProfileData), any())
- assertEquals(View.VISIBLE, workProfileFirstRunView.visibility)
- assertEquals(View.GONE, detectionNoticeView.visibility)
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_SCREENSHOT_PRIVATE_PROFILE_BEHAVIOR_FIX)
fun testOnScreenshotTakenUserHandle_withProfileProfileFirstRun() = runTest {
val profileData =
ProfileMessageController.ProfileFirstRunData(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
deleted file mode 100644
index 0847f01..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2022 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.screenshot
-
-import android.content.ComponentName
-import android.graphics.Bitmap
-import android.graphics.ColorSpace
-import android.graphics.Insets
-import android.graphics.Rect
-import android.hardware.HardwareBuffer
-import android.os.UserHandle
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
-import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import com.android.internal.util.ScreenshotRequest
-import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert
-import org.junit.Test
-
-private const val USER_ID = 1
-private const val TASK_ID = 1
-
-class RequestProcessorTest {
- private val imageCapture = FakeImageCapture()
- private val component = ComponentName("android.test", "android.test.Component")
- private val bounds = Rect(25, 25, 75, 75)
-
- private val policy = FakeScreenshotPolicy()
-
- @Test
- fun testFullScreenshot() = runBlocking {
- // Indicate that the primary content belongs to a normal user
- policy.setManagedProfile(USER_ID, false)
- policy.setDisplayContentInfo(
- policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
- )
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy)
-
- val processedData = processor.process(ScreenshotData.fromRequest(request))
-
- // Request has topComponent added, but otherwise unchanged.
- assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
- assertThat(processedData.source).isEqualTo(SCREENSHOT_OTHER)
- assertThat(processedData.topComponent).isEqualTo(component)
- }
-
- @Test
- fun testFullScreenshot_managedProfile() = runBlocking {
- // Provide a fake task bitmap when asked
- val bitmap = makeHardwareBitmap(100, 100)
- imageCapture.image = bitmap
-
- // Indicate that the primary content belongs to a manged profile
- policy.setManagedProfile(USER_ID, true)
- policy.setDisplayContentInfo(
- policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
- )
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy)
-
- val processedData = processor.process(ScreenshotData.fromRequest(request))
-
- // Expect a task snapshot is taken, overriding the full screen mode
- assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
- assertThat(processedData.bitmap).isEqualTo(bitmap)
- assertThat(processedData.screenBounds).isEqualTo(bounds)
- assertThat(processedData.insets).isEqualTo(Insets.NONE)
- assertThat(processedData.taskId).isEqualTo(TASK_ID)
- assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
- }
-
- @Test
- fun testFullScreenshot_managedProfile_nullBitmap() {
- // Provide a null task bitmap when asked
- imageCapture.image = null
-
- // Indicate that the primary content belongs to a manged profile
- policy.setManagedProfile(USER_ID, true)
- policy.setDisplayContentInfo(
- policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
- )
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy)
-
- Assert.assertThrows(IllegalStateException::class.java) {
- runBlocking { processor.process(ScreenshotData.fromRequest(request)) }
- }
- }
-
- @Test
- fun testProvidedImageScreenshot() = runBlocking {
- val bounds = Rect(50, 50, 150, 150)
- val processor = RequestProcessor(imageCapture, policy)
-
- policy.setManagedProfile(USER_ID, false)
-
- val bitmap = makeHardwareBitmap(100, 100)
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
- .setTopComponent(component)
- .setTaskId(TASK_ID)
- .setUserId(USER_ID)
- .setBitmap(bitmap)
- .setBoundsOnScreen(bounds)
- .setInsets(Insets.NONE)
- .build()
-
- val screenshotData = ScreenshotData.fromRequest(request)
- val processedData = processor.process(screenshotData)
-
- assertThat(processedData).isEqualTo(screenshotData)
- }
-
- @Test
- fun testProvidedImageScreenshot_managedProfile() = runBlocking {
- val bounds = Rect(50, 50, 150, 150)
- val processor = RequestProcessor(imageCapture, policy)
-
- // Indicate that the screenshot belongs to a manged profile
- policy.setManagedProfile(USER_ID, true)
-
- val bitmap = makeHardwareBitmap(100, 100)
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
- .setTopComponent(component)
- .setTaskId(TASK_ID)
- .setUserId(USER_ID)
- .setBitmap(bitmap)
- .setBoundsOnScreen(bounds)
- .setInsets(Insets.NONE)
- .build()
-
- val screenshotData = ScreenshotData.fromRequest(request)
- val processedData = processor.process(screenshotData)
-
- // Work profile, but already a task snapshot, so no changes
- assertThat(processedData).isEqualTo(screenshotData)
- }
-
- private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
- val buffer =
- HardwareBuffer.create(
- width,
- height,
- HardwareBuffer.RGBA_8888,
- 1,
- HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
- )
- return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
- }
-
- private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
- return bitmap.hardwareBuffer == this.hardwareBuffer && bitmap.colorSpace == this.colorSpace
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
deleted file mode 100644
index 5e07aef..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2023 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.screenshot
-
-import android.app.Notification
-import android.app.PendingIntent
-import android.content.ComponentName
-import android.content.Intent
-import android.graphics.Bitmap
-import android.graphics.drawable.Icon
-import android.net.Uri
-import android.os.UserHandle
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import java.util.concurrent.CompletableFuture
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNull
-import org.junit.Before
-import org.junit.Test
-
-@SmallTest
-class SaveImageInBackgroundTaskTest : SysuiTestCase() {
- private val imageExporter = mock<ImageExporter>()
- private val smartActions = mock<ScreenshotSmartActions>()
- private val smartActionsProvider = mock<ScreenshotNotificationSmartActionsProvider>()
- private val saveImageData = SaveImageInBackgroundTask.SaveImageInBackgroundData()
- private val testScreenshotId: String = "testScreenshotId"
- private val testBitmap = mock<Bitmap>()
- private val testUser = UserHandle.getUserHandleForUid(0)
- private val testIcon = mock<Icon>()
- private val testImageTime = 1234.toLong()
- private val flags = FakeFeatureFlags()
-
- private val smartActionsUriFuture = mock<CompletableFuture<List<Notification.Action>>>()
- private val smartActionsFuture = mock<CompletableFuture<List<Notification.Action>>>()
-
- private val testUri: Uri = Uri.parse("testUri")
- private val intent =
- Intent(Intent.ACTION_SEND)
- .setComponent(
- ComponentName.unflattenFromString(
- "com.google.android.test/com.google.android.test.TestActivity"
- )
- )
- private val immutablePendingIntent =
- PendingIntent.getBroadcast(
- mContext,
- 0,
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- private val mutablePendingIntent =
- PendingIntent.getBroadcast(
- mContext,
- 0,
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
- )
-
- private val saveImageTask =
- SaveImageInBackgroundTask(
- mContext,
- flags,
- imageExporter,
- smartActions,
- saveImageData,
- smartActionsProvider,
- )
-
- @Before
- fun setup() {
- whenever(
- smartActions.getSmartActionsFuture(
- eq(testScreenshotId),
- any(Uri::class.java),
- eq(testBitmap),
- eq(smartActionsProvider),
- any(ScreenshotSmartActionType::class.java),
- any(Boolean::class.java),
- eq(testUser)
- )
- )
- .thenReturn(smartActionsUriFuture)
- whenever(
- smartActions.getSmartActionsFuture(
- eq(testScreenshotId),
- eq(null),
- eq(testBitmap),
- eq(smartActionsProvider),
- any(ScreenshotSmartActionType::class.java),
- any(Boolean::class.java),
- eq(testUser)
- )
- )
- .thenReturn(smartActionsFuture)
- }
-
- @Test
- fun testQueryQuickShare_noAction() {
- whenever(
- smartActions.getSmartActions(
- eq(testScreenshotId),
- eq(smartActionsFuture),
- any(Int::class.java),
- eq(smartActionsProvider),
- eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
- )
- )
- .thenReturn(ArrayList<Notification.Action>())
-
- val quickShareAction =
- saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri)
-
- assertNull(quickShareAction)
- }
-
- @Test
- fun testQueryQuickShare_withActions() {
- val actions = ArrayList<Notification.Action>()
- actions.add(constructAction("Action One", mutablePendingIntent))
- actions.add(constructAction("Action Two", mutablePendingIntent))
- whenever(
- smartActions.getSmartActions(
- eq(testScreenshotId),
- eq(smartActionsUriFuture),
- any(Int::class.java),
- eq(smartActionsProvider),
- eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
- )
- )
- .thenReturn(actions)
-
- val quickShareAction =
- saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri)!!
-
- assertEquals("Action One", quickShareAction.title)
- assertEquals(mutablePendingIntent, quickShareAction.actionIntent)
- }
-
- @Test
- fun testCreateQuickShareAction_originalWasNull_returnsNull() {
- val quickShareAction =
- saveImageTask.createQuickShareAction(
- null,
- testScreenshotId,
- testUri,
- testImageTime,
- testBitmap,
- testUser
- )
-
- assertNull(quickShareAction)
- }
-
- @Test
- fun testCreateQuickShareAction_immutableIntentDifferentAction_returnsNull() {
- val actions = ArrayList<Notification.Action>()
- actions.add(constructAction("New Test Action", immutablePendingIntent))
- whenever(
- smartActions.getSmartActions(
- eq(testScreenshotId),
- eq(smartActionsUriFuture),
- any(Int::class.java),
- eq(smartActionsProvider),
- eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
- )
- )
- .thenReturn(actions)
- val origAction = constructAction("Old Test Action", immutablePendingIntent)
-
- val quickShareAction =
- saveImageTask.createQuickShareAction(
- origAction,
- testScreenshotId,
- testUri,
- testImageTime,
- testBitmap,
- testUser,
- )
-
- assertNull(quickShareAction)
- }
-
- @Test
- fun testCreateQuickShareAction_mutableIntent_returnsSafeIntent() {
- val actions = ArrayList<Notification.Action>()
- val action = constructAction("Action One", mutablePendingIntent)
- actions.add(action)
- whenever(
- smartActions.getSmartActions(
- eq(testScreenshotId),
- eq(smartActionsUriFuture),
- any(Int::class.java),
- eq(smartActionsProvider),
- eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
- )
- )
- .thenReturn(actions)
-
- val quickShareAction =
- saveImageTask.createQuickShareAction(
- constructAction("Test Action", mutablePendingIntent),
- testScreenshotId,
- testUri,
- testImageTime,
- testBitmap,
- testUser
- )
- val quickSharePendingIntent =
- quickShareAction.actionIntent.intent.extras!!.getParcelable(
- SmartActionsReceiver.EXTRA_ACTION_INTENT,
- PendingIntent::class.java
- )
-
- assertEquals("Test Action", quickShareAction.title)
- assertEquals(mutablePendingIntent, quickSharePendingIntent)
- }
-
- @Test
- fun testCreateQuickShareAction_immutableIntent_returnsSafeIntent() {
- val actions = ArrayList<Notification.Action>()
- val action = constructAction("Test Action", immutablePendingIntent)
- actions.add(action)
- whenever(
- smartActions.getSmartActions(
- eq(testScreenshotId),
- eq(smartActionsUriFuture),
- any(Int::class.java),
- eq(smartActionsProvider),
- eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
- )
- )
- .thenReturn(actions)
-
- val quickShareAction =
- saveImageTask.createQuickShareAction(
- constructAction("Test Action", immutablePendingIntent),
- testScreenshotId,
- testUri,
- testImageTime,
- testBitmap,
- testUser,
- )!!
-
- assertEquals("Test Action", quickShareAction.title)
- assertEquals(
- immutablePendingIntent,
- quickShareAction.actionIntent.intent.extras!!.getParcelable(
- SmartActionsReceiver.EXTRA_ACTION_INTENT,
- PendingIntent::class.java
- )
- )
- }
-
- private fun constructAction(title: String, intent: PendingIntent): Notification.Action {
- return Notification.Action.Builder(testIcon, title, intent).build()
- }
-}
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 a8d5008..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,68 @@
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
+ @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
+ public void appClipsLaunched_backlinks_multipleBacklinksAvailable_duplicateName()
+ throws RemoteException {
+ // Set up mocking for multiple backlinks.
+ ResolveInfo resolveInfo1 = createBacklinksTaskResolveInfo();
+
+ ResolveInfo resolveInfo2 = createBacklinksTaskResolveInfo();
+ RunningTaskInfo runningTaskInfo2 = createTaskInfoForBacklinksTask();
+ int taskId2 = BACKLINKS_TASK_ID + 2;
+ runningTaskInfo2.taskId = taskId2;
+
+ when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
+ mDisplayIdCaptor.capture()))
+ .thenReturn(List.of(TASK_THAT_SUPPORTS_BACKLINKS, runningTaskInfo2));
+ when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(resolveInfo1,
+ resolveInfo1, resolveInfo1, resolveInfo2, resolveInfo2, resolveInfo2);
+ when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+
+ // Using same AssistContent data for both tasks.
+ mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, BACKLINKS_TASK_ID);
+ mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, taskId2);
+
+ // Mocking complete, trigger backlinks.
+ launchActivity();
+ waitForIdleSync();
+
+ // Verify default backlink shown to user has the numerical suffix.
+ 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 {
@@ -339,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);
@@ -360,7 +433,7 @@
}
private void runOnMainThread(Runnable runnable) {
- mContext.getMainExecutor().execute(runnable);
+ getContext().getMainExecutor().execute(runnable);
}
private static ResolveInfo createBacklinksTaskResolveInfo() {
@@ -384,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..886b32b 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
@@ -24,13 +24,16 @@
import static android.content.Intent.ACTION_VIEW;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
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.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.reset;
@@ -43,6 +46,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 +66,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;
@@ -69,6 +75,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -92,30 +99,45 @@
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;
+ private ArgumentCaptor<Intent> mPackageManagerLauncherIntentCaptor;
+ private ArgumentCaptor<Intent> mPackageManagerBacklinkIntentCaptor;
private AppClipsViewModel mViewModel;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mPackageManagerIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+ mPackageManagerLauncherIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+ mPackageManagerBacklinkIntentCaptor = ArgumentCaptor.forClass(Intent.class);
// Set up mocking for backlinks.
when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY))
.thenReturn(List.of(createTaskInfoForBacklinksTask()));
- when(mPackageManager.resolveActivity(mPackageManagerIntentCaptor.capture(), anyInt()))
- .thenReturn(createBacklinksTaskResolveInfo());
+ ResolveInfo expectedResolveInfo = createBacklinksTaskResolveInfo();
+ when(mPackageManager.resolveActivity(mPackageManagerLauncherIntentCaptor.capture(),
+ anyInt())).thenReturn(expectedResolveInfo);
+ when(mPackageManager.queryIntentActivities(mPackageManagerBacklinkIntentCaptor.capture(),
+ eq(MATCH_DEFAULT_ONLY))).thenReturn(List.of(expectedResolveInfo));
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
@@ -195,11 +217,11 @@
mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
waitForIdleSync();
- Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
+ Intent queriedIntent = mPackageManagerBacklinkIntentCaptor.getValue();
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();
@@ -212,6 +234,63 @@
}
@Test
+ public void triggerBacklinks_shouldUpdateBacklinks_withUriForDifferentApp() {
+ Uri expectedUri = Uri.parse("https://android.com");
+ AssistContent contentWithUri = new AssistContent();
+ contentWithUri.setWebUri(expectedUri);
+ mockForAssistContent(contentWithUri, BACKLINKS_TASK_ID);
+
+ // Reset PackageManager mocking done in setup.
+ reset(mPackageManager);
+ String package2 = BACKLINKS_TASK_PACKAGE_NAME + 2;
+ String appName2 = BACKLINKS_TASK_APP_NAME + 2;
+ ResolveInfo resolveInfo2 = createBacklinksTaskResolveInfo();
+ ActivityInfo activityInfo2 = resolveInfo2.activityInfo;
+ activityInfo2.name = appName2;
+ activityInfo2.packageName = package2;
+ activityInfo2.applicationInfo.packageName = package2;
+
+ Intent app2LauncherIntent = new Intent(ACTION_MAIN).addCategory(
+ CATEGORY_LAUNCHER).setPackage(package2);
+ when(mPackageManager.resolveActivity(intentEquals(app2LauncherIntent), eq(/* flags= */ 0)))
+ .thenReturn(resolveInfo2);
+ Intent uriIntent = new Intent(ACTION_VIEW).setData(expectedUri);
+ when(mPackageManager.queryIntentActivities(intentEquals(uriIntent), eq(MATCH_DEFAULT_ONLY)))
+ .thenReturn(List.of(resolveInfo2));
+ when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue();
+ ClipData clipData = result.getClipData();
+ ClipDescription resultDescription = clipData.getDescription();
+ assertThat(resultDescription.getLabel().toString()).isEqualTo(appName2);
+ assertThat(resultDescription.getMimeType(0)).isEqualTo(MIMETYPE_TEXT_URILIST);
+ assertThat(clipData.getItemCount()).isEqualTo(1);
+ assertThat(clipData.getItemAt(0).getUri()).isEqualTo(expectedUri);
+
+ assertThat(mViewModel.getBacklinksLiveData().getValue().size()).isEqualTo(1);
+ }
+
+ private static class IntentMatcher implements ArgumentMatcher<Intent> {
+ private final Intent mExpectedIntent;
+
+ IntentMatcher(Intent expectedIntent) {
+ mExpectedIntent = expectedIntent;
+ }
+
+ @Override
+ public boolean matches(Intent actualIntent) {
+ return actualIntent != null && mExpectedIntent.filterEquals(actualIntent);
+ }
+ }
+
+ private static Intent intentEquals(Intent intent) {
+ return argThat(new IntentMatcher(intent));
+ }
+
+ @Test
public void triggerBacklinks_withNonResolvableUri_usesMainLauncherIntent() {
Uri expectedUri = Uri.parse("https://developers.android.com");
AssistContent contentWithUri = new AssistContent();
@@ -235,10 +314,10 @@
mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
waitForIdleSync();
- Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
+ Intent queriedIntent = mPackageManagerBacklinkIntentCaptor.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();
@@ -269,7 +348,7 @@
mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
waitForIdleSync();
- Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
+ Intent queriedIntent = mPackageManagerLauncherIntentCaptor.getValue();
assertThat(queriedIntent.getPackage()).isEqualTo(BACKLINKS_TASK_PACKAGE_NAME);
assertThat(queriedIntent.getAction()).isEqualTo(ACTION_MAIN);
assertThat(queriedIntent.getCategories()).containsExactly(CATEGORY_LAUNCHER);
@@ -307,6 +386,8 @@
@Test
public void triggerBacklinks_taskIdsToIgnoreConsidered_noBacklinkAvailable() {
+ mockForAssistContent(EMPTY_ASSIST_CONTENT, BACKLINKS_TASK_ID);
+
mViewModel.triggerBacklinks(Set.of(BACKLINKS_TASK_ID), DEFAULT_DISPLAY);
waitForIdleSync();
@@ -340,7 +421,9 @@
// For each task, the logic queries PM 3 times, twice for verifying if an app can be
// launched via launcher and once with the data provided in backlink intent.
when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo1,
- resolveInfo1, resolveInfo1, resolveInfo2, resolveInfo2, resolveInfo2);
+ resolveInfo1, resolveInfo2, resolveInfo2);
+ when(mPackageManager.queryIntentActivities(any(Intent.class), eq(MATCH_DEFAULT_ONLY)))
+ .thenReturn(List.of(resolveInfo1)).thenReturn(List.of(resolveInfo2));
when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY))
.thenReturn(List.of(runningTaskInfo1, runningTaskInfo2));
@@ -362,16 +445,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 +514,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 +561,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/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index 263b001..78764c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -79,21 +79,6 @@
@Test
fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() = runTest {
- tracker =
- UserTrackerImpl(
- context,
- { fakeFeatures },
- userManager,
- iActivityManager,
- dumpManager,
- this,
- testDispatcher,
- handler
- )
- tracker.initialize(0)
- tracker.addCallback(callback, executor)
- val profileID = tracker.userId + 10
-
`when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
val id = invocation.getArgument<Int>(0)
val info = UserInfo(id, "", UserInfo.FLAG_FULL)
@@ -109,6 +94,21 @@
listOf(info, infoProfile)
}
+ tracker =
+ UserTrackerImpl(
+ context,
+ { fakeFeatures },
+ userManager,
+ iActivityManager,
+ dumpManager,
+ this,
+ testDispatcher,
+ handler
+ )
+ tracker.initialize(0)
+ tracker.addCallback(callback, executor)
+ val profileID = tracker.userId + 10
+
tracker.onReceive(context, Intent(intentAction))
verify(callback, times(0)).onUserChanged(anyInt(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 774aa51..2e2ac3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -33,9 +33,11 @@
import com.android.systemui.flags.Flags
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.TruthJUnit.assume
+import java.util.concurrent.Executor
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -54,10 +56,7 @@
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
-
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -71,27 +70,19 @@
fun isBackgroundUserTrackerEnabled(): Iterable<Boolean> = listOf(true, false)
}
- @Mock
- private lateinit var context: Context
+ @Mock private lateinit var context: Context
- @Mock
- private lateinit var userManager: UserManager
+ @Mock private lateinit var userManager: UserManager
- @Mock
- private lateinit var iActivityManager: IActivityManager
+ @Mock private lateinit var iActivityManager: IActivityManager
- @Mock
- private lateinit var userSwitchingReply: IRemoteCallback
+ @Mock private lateinit var userSwitchingReply: IRemoteCallback
- @Mock(stubOnly = true)
- private lateinit var dumpManager: DumpManager
+ @Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
- @Mock(stubOnly = true)
- private lateinit var handler: Handler
+ @Mock(stubOnly = true) private lateinit var handler: Handler
- @Parameterized.Parameter
- @JvmField
- var isBackgroundUserTrackerEnabled: Boolean = false
+ @Parameterized.Parameter @JvmField var isBackgroundUserTrackerEnabled: Boolean = false
private val testScope = TestScope()
private val testDispatcher = StandardTestDispatcher(testScope.testScheduler)
@@ -104,365 +95,379 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(context.userId).thenReturn(UserHandle.USER_SYSTEM)
- `when`(context.user).thenReturn(UserHandle.SYSTEM)
- `when`(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation ->
+ whenever(context.userId).thenReturn(UserHandle.USER_SYSTEM)
+ whenever(context.user).thenReturn(UserHandle.SYSTEM)
+ whenever(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation ->
val user = invocation.getArgument<UserHandle>(0)
- `when`(context.user).thenReturn(user)
- `when`(context.userId).thenReturn(user.identifier)
+ whenever(context.user).thenReturn(user)
+ whenever(context.userId).thenReturn(user.identifier)
context
}
- `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
val info = UserInfo(invocation.getArgument<Int>(0), "", UserInfo.FLAG_FULL)
listOf(info)
}
featureFlags.set(Flags.USER_TRACKER_BACKGROUND_CALLBACKS, isBackgroundUserTrackerEnabled)
tracker =
- UserTrackerImpl(
- context,
- { featureFlags },
- userManager,
- iActivityManager,
- dumpManager,
- testScope.backgroundScope,
- testDispatcher,
- handler,
- )
+ UserTrackerImpl(
+ context,
+ { featureFlags },
+ userManager,
+ iActivityManager,
+ dumpManager,
+ testScope.backgroundScope,
+ testDispatcher,
+ handler,
+ )
}
+ @Test fun testNotInitialized() = testScope.runTest { assertThat(tracker.initialized).isFalse() }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserIdBeforeInitThrowsException() = testScope.runTest { tracker.userId }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest { tracker.userHandle }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserContextBeforeInitThrowsException() = testScope.runTest { tracker.userContext }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserContentResolverBeforeInitThrowsException() =
+ testScope.runTest { tracker.userContentResolver }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest { tracker.userProfiles }
+
@Test
- fun testNotInitialized() = testScope.runTest {
- assertThat(tracker.initialized).isFalse()
- }
+ fun testInitialize() =
+ testScope.runTest {
+ tracker.initialize(0)
- @Test(expected = IllegalStateException::class)
- fun testGetUserIdBeforeInitThrowsException() = testScope.runTest {
- tracker.userId
- }
-
- @Test(expected = IllegalStateException::class)
- fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest {
- tracker.userHandle
- }
-
- @Test(expected = IllegalStateException::class)
- fun testGetUserContextBeforeInitThrowsException() = testScope.runTest {
- tracker.userContext
- }
-
- @Test(expected = IllegalStateException::class)
- fun testGetUserContentResolverBeforeInitThrowsException() = testScope.runTest {
- tracker.userContentResolver
- }
-
- @Test(expected = IllegalStateException::class)
- fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest {
- tracker.userProfiles
- }
+ assertThat(tracker.initialized).isTrue()
+ }
@Test
- fun testInitialize() = testScope.runTest {
- tracker.initialize(0)
+ fun testReceiverRegisteredOnInitialize() =
+ testScope.runTest {
+ tracker.initialize(0)
- assertThat(tracker.initialized).isTrue()
- }
+ val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
- @Test
- fun testReceiverRegisteredOnInitialize() = testScope.runTest {
- tracker.initialize(0)
-
- val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
-
- verify(context)
+ verify(context)
.registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
- with(captor.value) {
- assertThat(countActions()).isEqualTo(11)
- assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue()
- assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
- assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
- assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
- assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
- assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
- assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
- assertThat(hasAction(Intent.ACTION_PROFILE_ADDED)).isTrue()
- assertThat(hasAction(Intent.ACTION_PROFILE_REMOVED)).isTrue()
- assertThat(hasAction(Intent.ACTION_PROFILE_AVAILABLE)).isTrue()
- assertThat(hasAction(Intent.ACTION_PROFILE_UNAVAILABLE)).isTrue()
- }
- }
-
- @Test
- fun testInitialValuesSet() = testScope.runTest {
- val testID = 4
- tracker.initialize(testID)
-
- verify(userManager).getProfiles(testID)
-
- assertThat(tracker.userId).isEqualTo(testID)
- assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID))
- assertThat(tracker.userContext.userId).isEqualTo(testID)
- assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID))
- assertThat(tracker.userProfiles).hasSize(1)
-
- val info = tracker.userProfiles[0]
- assertThat(info.id).isEqualTo(testID)
- }
-
- @Test
- fun testUserSwitch() = testScope.runTest {
- tracker.initialize(0)
- val newID = 5
-
- val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
- verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
- captor.value.onUserSwitching(newID, userSwitchingReply)
- runCurrent()
- verify(userSwitchingReply).sendResult(any())
-
- verify(userManager).getProfiles(newID)
-
- assertThat(tracker.userId).isEqualTo(newID)
- assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID))
- assertThat(tracker.userContext.userId).isEqualTo(newID)
- assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID))
- assertThat(tracker.userProfiles).hasSize(1)
-
- val info = tracker.userProfiles[0]
- assertThat(info.id).isEqualTo(newID)
- }
-
- @Test
- fun testManagedProfileAvailable() = testScope.runTest {
- tracker.initialize(0)
- val profileID = tracker.userId + 10
-
- `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
- val id = invocation.getArgument<Int>(0)
- val info = UserInfo(id, "", UserInfo.FLAG_FULL)
- val infoProfile = UserInfo(
- id + 10,
- "",
- "",
- UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
- )
- infoProfile.profileGroupId = id
- listOf(info, infoProfile)
+ with(captor.value) {
+ assertThat(countActions()).isEqualTo(11)
+ assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue()
+ assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
+ assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
+ assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
+ assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
+ assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
+ assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
+ assertThat(hasAction(Intent.ACTION_PROFILE_ADDED)).isTrue()
+ assertThat(hasAction(Intent.ACTION_PROFILE_REMOVED)).isTrue()
+ assertThat(hasAction(Intent.ACTION_PROFILE_AVAILABLE)).isTrue()
+ assertThat(hasAction(Intent.ACTION_PROFILE_UNAVAILABLE)).isTrue()
+ }
}
- val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
- tracker.onReceive(context, intent)
-
- assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
- }
-
@Test
- fun testManagedProfileUnavailable() = testScope.runTest {
- tracker.initialize(0)
- val profileID = tracker.userId + 10
+ fun testInitialValuesSet() =
+ testScope.runTest {
+ val testID = 4
+ tracker.initialize(testID)
- `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
- val id = invocation.getArgument<Int>(0)
- val info = UserInfo(id, "", UserInfo.FLAG_FULL)
- val infoProfile = UserInfo(
- id + 10,
- "",
- "",
- UserInfo.FLAG_MANAGED_PROFILE or UserInfo.FLAG_QUIET_MODE,
- UserManager.USER_TYPE_PROFILE_MANAGED
- )
- infoProfile.profileGroupId = id
- listOf(info, infoProfile)
+ verify(userManager).getProfiles(testID)
+
+ assertThat(tracker.userId).isEqualTo(testID)
+ assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID))
+ assertThat(tracker.userContext.userId).isEqualTo(testID)
+ assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID))
+ assertThat(tracker.userProfiles).hasSize(1)
+
+ val info = tracker.userProfiles[0]
+ assertThat(info.id).isEqualTo(testID)
}
- val intent = Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
- tracker.onReceive(context, intent)
-
- assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
- }
-
@Test
- fun testManagedProfileStartedAndRemoved() = testScope.runTest {
- tracker.initialize(0)
- val profileID = tracker.userId + 10
+ fun testUserSwitch() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val newID = 5
- `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
- val id = invocation.getArgument<Int>(0)
- val info = UserInfo(id, "", UserInfo.FLAG_FULL)
- val infoProfile = UserInfo(
- id + 10,
- "",
- "",
- UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
- )
- infoProfile.profileGroupId = id
- listOf(info, infoProfile)
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onBeforeUserSwitching(newID)
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
+ verify(userSwitchingReply).sendResult(any())
+
+ verify(userManager).getProfiles(newID)
+
+ assertThat(tracker.userId).isEqualTo(newID)
+ assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID))
+ assertThat(tracker.userContext.userId).isEqualTo(newID)
+ assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID))
+ assertThat(tracker.userProfiles).hasSize(1)
+
+ val info = tracker.userProfiles[0]
+ assertThat(info.id).isEqualTo(newID)
}
- // Managed profile started
- val intent = Intent(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
- tracker.onReceive(context, intent)
+ @Test
+ fun testManagedProfileAvailable() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val profileID = tracker.userId + 10
- assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
+ whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val id = invocation.getArgument<Int>(0)
+ val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+ val infoProfile =
+ UserInfo(
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
+ )
+ infoProfile.profileGroupId = id
+ listOf(info, infoProfile)
+ }
- `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
- listOf(UserInfo(invocation.getArgument(0), "", UserInfo.FLAG_FULL))
+ val intent =
+ Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ tracker.onReceive(context, intent)
+
+ assertThat(tracker.userProfiles.map { it.id })
+ .containsExactly(tracker.userId, profileID)
}
- val intent2 = Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
- tracker.onReceive(context, intent2)
-
- assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId)
- }
-
@Test
- fun testCallbackNotCalledOnAdd() = testScope.runTest {
- tracker.initialize(0)
- val callback = TestCallback()
+ fun testManagedProfileUnavailable() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val profileID = tracker.userId + 10
- tracker.addCallback(callback, executor)
+ whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val id = invocation.getArgument<Int>(0)
+ val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+ val infoProfile =
+ UserInfo(
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE or UserInfo.FLAG_QUIET_MODE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
+ )
+ infoProfile.profileGroupId = id
+ listOf(info, infoProfile)
+ }
- assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
- assertThat(callback.calledOnUserChanged).isEqualTo(0)
- }
+ val intent =
+ Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ tracker.onReceive(context, intent)
- @Test
- fun testCallbackCalledOnUserChanging() = testScope.runTest {
- tracker.initialize(0)
- val callback = TestCallback()
- tracker.addCallback(callback, executor)
-
- val newID = 5
-
- val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
- verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
- captor.value.onUserSwitching(newID, userSwitchingReply)
- runCurrent()
-
- verify(userSwitchingReply).sendResult(any())
- assertThat(callback.calledOnUserChanging).isEqualTo(1)
- assertThat(callback.lastUser).isEqualTo(newID)
- assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
- }
-
- @Test
- fun testAsyncCallbackWaitsUserToChange() = testScope.runTest {
- // Skip this test for CountDownLatch variation. The problem is that there would be a
- // deadlock if the callbacks processing runs on the same thread as the callback (which
- // is blocked by the latch). Before the change it works because the callbacks are
- // processed on a binder thread which is always distinct.
- // This is the issue that this feature addresses.
- assume().that(isBackgroundUserTrackerEnabled).isTrue()
-
- tracker.initialize(0)
- val callback = TestCallback()
- val callbackExecutor = FakeExecutor(FakeSystemClock())
- tracker.addCallback(callback, callbackExecutor)
-
- val newID = 5
-
- val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
- verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onUserSwitching(newID, userSwitchingReply)
-
- assertThat(callback.calledOnUserChanging).isEqualTo(0)
- verify(userSwitchingReply, never()).sendResult(any())
-
- FakeExecutor.exhaustExecutors(callbackExecutor)
- runCurrent()
- FakeExecutor.exhaustExecutors(callbackExecutor)
- runCurrent()
-
- assertThat(callback.calledOnUserChanging).isEqualTo(1)
- verify(userSwitchingReply).sendResult(any())
- }
-
- @Test
- fun testCallbackCalledOnUserChanged() = testScope.runTest {
- tracker.initialize(0)
- val callback = TestCallback()
- tracker.addCallback(callback, executor)
-
- val newID = 5
-
- val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
- verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
- captor.value.onUserSwitchComplete(newID)
- runCurrent()
-
- assertThat(callback.calledOnUserChanged).isEqualTo(1)
- assertThat(callback.lastUser).isEqualTo(newID)
- assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
- assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
- assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID)
- }
-
- @Test
- fun testCallbackCalledOnUserInfoChanged() = testScope.runTest {
- tracker.initialize(0)
- val callback = TestCallback()
- tracker.addCallback(callback, executor)
- val profileID = tracker.userId + 10
-
- `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
- val id = invocation.getArgument<Int>(0)
- val info = UserInfo(id, "", UserInfo.FLAG_FULL)
- val infoProfile = UserInfo(
- id + 10,
- "",
- "",
- UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
- )
- infoProfile.profileGroupId = id
- listOf(info, infoProfile)
+ assertThat(tracker.userProfiles.map { it.id })
+ .containsExactly(tracker.userId, profileID)
}
- val intent = Intent(Intent.ACTION_USER_INFO_CHANGED)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ @Test
+ fun testManagedProfileStartedAndRemoved() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val profileID = tracker.userId + 10
- tracker.onReceive(context, intent)
+ whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val id = invocation.getArgument<Int>(0)
+ val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+ val infoProfile =
+ UserInfo(
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
+ )
+ infoProfile.profileGroupId = id
+ listOf(info, infoProfile)
+ }
- assertThat(callback.calledOnUserChanged).isEqualTo(0)
- assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
- assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
- }
+ // Managed profile started
+ val intent =
+ Intent(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ tracker.onReceive(context, intent)
+
+ assertThat(tracker.userProfiles.map { it.id })
+ .containsExactly(tracker.userId, profileID)
+
+ whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ listOf(UserInfo(invocation.getArgument(0), "", UserInfo.FLAG_FULL))
+ }
+
+ val intent2 =
+ Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ tracker.onReceive(context, intent2)
+
+ assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId)
+ }
@Test
- fun testCallbackRemoved() = testScope.runTest {
- tracker.initialize(0)
- val newID = 5
- val profileID = newID + 10
+ fun testCallbackNotCalledOnAdd() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val callback = TestCallback()
- val callback = TestCallback()
- tracker.addCallback(callback, executor)
- tracker.removeCallback(callback)
+ tracker.addCallback(callback, executor)
- val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
- verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onUserSwitching(newID, userSwitchingReply)
- runCurrent()
- verify(userSwitchingReply).sendResult(any())
- captor.value.onUserSwitchComplete(newID)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+ assertThat(callback.calledOnUserChanged).isEqualTo(0)
+ }
- val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ @Test
+ fun testCallbackCalledOnUserChanging() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
- tracker.onReceive(context, intentProfiles)
+ val newID = 5
- assertThat(callback.calledOnUserChanging).isEqualTo(0)
- assertThat(callback.calledOnUserChanged).isEqualTo(0)
- assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
- }
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onBeforeUserSwitching(newID)
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
+
+ verify(userSwitchingReply).sendResult(any())
+ assertThat(callback.calledOnUserChanging).isEqualTo(1)
+ assertThat(callback.lastUser).isEqualTo(newID)
+ assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+ }
+
+ @Test
+ fun testAsyncCallbackWaitsUserToChange() =
+ testScope.runTest {
+ // Skip this test for CountDownLatch variation. The problem is that there would be a
+ // deadlock if the callbacks processing runs on the same thread as the callback (which
+ // is blocked by the latch). Before the change it works because the callbacks are
+ // processed on a binder thread which is always distinct.
+ // This is the issue that this feature addresses.
+ assume().that(isBackgroundUserTrackerEnabled).isTrue()
+
+ tracker.initialize(0)
+ val callback = TestCallback()
+ val callbackExecutor = FakeExecutor(FakeSystemClock())
+ tracker.addCallback(callback, callbackExecutor)
+
+ val newID = 5
+
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(0)
+ verify(userSwitchingReply, never()).sendResult(any())
+
+ FakeExecutor.exhaustExecutors(callbackExecutor)
+ runCurrent()
+ FakeExecutor.exhaustExecutors(callbackExecutor)
+ runCurrent()
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(1)
+ verify(userSwitchingReply).sendResult(any())
+ }
+
+ @Test
+ fun testCallbackCalledOnUserChanged() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+
+ val newID = 5
+
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onBeforeUserSwitching(newID)
+ captor.value.onUserSwitchComplete(newID)
+ runCurrent()
+
+ assertThat(callback.calledOnUserChanged).isEqualTo(1)
+ assertThat(callback.lastUser).isEqualTo(newID)
+ assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+ assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID)
+ }
+
+ @Test
+ fun testCallbackCalledOnUserInfoChanged() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+ val profileID = tracker.userId + 10
+
+ whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val id = invocation.getArgument<Int>(0)
+ val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+ val infoProfile =
+ UserInfo(
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
+ )
+ infoProfile.profileGroupId = id
+ listOf(info, infoProfile)
+ }
+
+ val intent =
+ Intent(Intent.ACTION_USER_INFO_CHANGED)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+ tracker.onReceive(context, intent)
+
+ assertThat(callback.calledOnUserChanged).isEqualTo(0)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+ assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
+ }
+
+ @Test
+ fun testCallbackRemoved() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val newID = 5
+ val profileID = newID + 10
+
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+ tracker.removeCallback(callback)
+
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
+ verify(userSwitchingReply).sendResult(any())
+ captor.value.onUserSwitchComplete(newID)
+
+ val intentProfiles =
+ Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+ tracker.onReceive(context, intentProfiles)
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(0)
+ assertThat(callback.calledOnUserChanged).isEqualTo(0)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+ }
private class TestCallback : UserTracker.Callback {
var calledOnUserChanging = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index c8ff52a..c0444fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -61,7 +61,6 @@
import com.android.systemui.media.controls.controller.keyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
@@ -118,7 +117,8 @@
object : AmbientTouchComponent.Factory {
override fun create(
lifecycleOwner: LifecycleOwner,
- touchHandlers: Set<TouchHandler>
+ touchHandlers: Set<TouchHandler>,
+ loggingName: String
): AmbientTouchComponent =
object : AmbientTouchComponent {
override fun getTouchMonitor(): TouchMonitor = touchMonitor
@@ -706,7 +706,7 @@
verify(containerView).onTouchEvent(DOWN_EVENT)
// User is interacting with shade on lockscreen.
- fakeShadeRepository.setLegacyLockscreenShadeTracking(true)
+ shadeTestUtil.setLockscreenShadeTracking(true)
testableLooper.processAllMessages()
// A move event is ignored while the user is already interacting.
@@ -734,7 +734,7 @@
.thenReturn(true)
// Shade is open slightly.
- fakeShadeRepository.setLegacyShadeExpansion(0.01f)
+ shadeTestUtil.setShadeExpansion(0.01f)
testableLooper.processAllMessages()
// Touches are not consumed.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 9481e5a..e0c4ab7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -431,7 +431,6 @@
mFakeKeyguardRepository,
mKeyguardTransitionInteractor,
mPowerInteractor,
- mShadeRepository,
new FakeUserSetupRepository(),
mock(UserSwitcherInteractor.class),
new ShadeInteractorLegacyImpl(
@@ -447,8 +446,8 @@
() -> mLargeScreenHeaderHelper
),
mShadeRepository
- )
- );
+ ),
+ mKosmos.getShadeModeInteractor());
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(
mUiEventLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 3f6617b..a52f173 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -217,7 +217,6 @@
mKeyguardRepository,
keyguardTransitionInteractor,
powerInteractor,
- mShadeRepository,
new FakeUserSetupRepository(),
mUserSwitcherInteractor,
new ShadeInteractorLegacyImpl(
@@ -232,8 +231,8 @@
deviceEntryUdfpsInteractor,
() -> mLargeScreenHeaderHelper),
mShadeRepository
- )
- );
+ ),
+ mKosmos.getShadeModeInteractor());
mActiveNotificationsInteractor = new ActiveNotificationsInteractor(
new ActiveNotificationListRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index fadb1d7..b65a902 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -156,6 +156,25 @@
}
@Test
+ fun qsFullscreen_falseWhenIdleSplitShadeQs() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isQsFullscreen)
+
+ // WHEN split shade is enabled and Idle on QuickSettings scene
+ shadeTestUtil.setSplitShade(true)
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.QuickSettings)
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN QS is not fullscreen
+ Truth.assertThat(actual).isFalse()
+ }
+
+ @Test
fun qsFullscreen_trueWhenIdleQS() =
testScope.runTest {
val actual by collectLastValue(underTest.isQsFullscreen)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index 396d017..d6b3b91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -28,7 +28,7 @@
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.tuner.TunerService
@@ -71,7 +71,6 @@
private val airplaneModeRepository = FakeAirplaneModeRepository()
private val connectivityRepository = FakeConnectivityRepository()
- private val mobileConnectionsRepository = FakeMobileConnectionsRepository()
@Before
fun setup() {
@@ -81,7 +80,7 @@
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
- mobileConnectionsRepository,
+ kosmos.fakeMobileConnectionsRepository,
)
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index a0d231b..60a1855 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -51,6 +51,7 @@
import android.platform.test.annotations.EnableFlags;
import android.service.notification.StatusBarNotification;
import android.view.ViewGroup;
+import android.widget.ImageView;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -431,6 +432,32 @@
mIconView.getIconScale(), 0.01f);
}
+ @Test
+ @EnableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
+ public void set_iconThatWantsFixedSpace_setsScaleType() {
+ mIconView.setScaleType(ImageView.ScaleType.FIT_START);
+ StatusBarIcon icon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+ Icon.createWithResource(mContext, R.drawable.ic_android), 0, 0, "",
+ StatusBarIcon.Type.SystemIcon, StatusBarIcon.Shape.FIXED_SPACE);
+
+ mIconView.set(icon);
+
+ assertThat(mIconView.getScaleType()).isEqualTo(ImageView.ScaleType.FIT_CENTER);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
+ public void set_iconWithOtherShape_keepsScaleType() {
+ mIconView.setScaleType(ImageView.ScaleType.FIT_START);
+ StatusBarIcon icon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+ Icon.createWithResource(mContext, R.drawable.ic_android), 0, 0, "",
+ StatusBarIcon.Type.SystemIcon, StatusBarIcon.Shape.WRAP_CONTENT);
+
+ mIconView.set(icon);
+
+ assertThat(mIconView.getScaleType()).isEqualTo(ImageView.ScaleType.FIT_START);
+ }
+
private static StatusBarNotification getMockSbn() {
StatusBarNotification sbn = mock(StatusBarNotification.class);
when(sbn.getNotification()).thenReturn(mock(Notification.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
index 8576893..118dea6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.commandline.commandRegistry
@@ -103,6 +104,22 @@
@Test
@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ fun chip_supportsColor() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ commandRegistry.onShellCommand(
+ pw,
+ arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343")
+ )
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).colors)
+ .isInstanceOf(ColorsModel.Custom::class.java)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_hasHideArg_hidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index bd5df07..26ce7b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -19,8 +19,9 @@
import android.content.DialogInterface
import android.content.packageManager
import android.content.pm.PackageManager
+import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
-import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.DisableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -39,11 +40,9 @@
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip
import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
-import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -51,8 +50,6 @@
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.StringWriter
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -69,21 +66,22 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+/**
+ * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled.
+ */
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
+@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val systemClock = kosmos.fakeSystemClock
- private val commandRegistry = kosmos.commandRegistry
private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
private val callRepo = kosmos.ongoingCallRepository
- private val pw = PrintWriter(StringWriter())
-
private val mockSystemUIDialog = mock<SystemUIDialog>()
private val chipBackgroundView = mock<ChipBackgroundContainer>()
private val chipView =
@@ -102,74 +100,78 @@
fun setUp() {
setUpPackageManagerForMediaProjection(kosmos)
kosmos.demoRonChipViewModel.start()
- whenever(kosmos.packageManager.getApplicationIcon(any<String>()))
- .thenReturn(BitmapDrawable())
+ val icon =
+ BitmapDrawable(
+ context.resources,
+ Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
+ )
+ whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
}
@Test
- fun chip_allHidden_hidden() =
+ fun primaryChip_allHidden_hidden() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.DoingNothing
mediaProjectionState.value = MediaProjectionState.NotProjecting
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@Test
- fun chip_screenRecordShow_restHidden_screenRecordShown() =
+ fun primaryChip_screenRecordShow_restHidden_screenRecordShown() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value = MediaProjectionState.NotProjecting
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
assertIsScreenRecordChip(latest)
}
@Test
- fun chip_screenRecordShowAndCallShow_screenRecordShown() =
+ fun primaryChip_screenRecordShowAndCallShow_screenRecordShown() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.Recording
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
assertIsScreenRecordChip(latest)
}
@Test
- fun chip_screenRecordShowAndShareToAppShow_screenRecordShown() =
+ fun primaryChip_screenRecordShowAndShareToAppShow_screenRecordShown() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
assertIsScreenRecordChip(latest)
}
@Test
- fun chip_shareToAppShowAndCallShow_shareToAppShown() =
+ fun primaryChip_shareToAppShowAndCallShow_shareToAppShown() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.DoingNothing
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
assertIsShareToAppChip(latest)
}
@Test
- fun chip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
+ fun primaryChip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.DoingNothing
// MediaProjection covers both share-to-app and cast-to-other-device
@@ -177,30 +179,22 @@
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
assertIsCallChip(latest)
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
- fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
+ fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
testScope.runTest {
// Start with just the lowest priority chip shown
- addDemoRonChip(commandRegistry, pw)
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
// And everything else hidden
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
mediaProjectionState.value = MediaProjectionState.NotProjecting
screenRecordState.value = ScreenRecordModel.DoingNothing
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
- assertIsDemoRonChip(latest)
-
- // WHEN the higher priority call chip is added
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
-
- // THEN the higher priority call chip is used
assertIsCallChip(latest)
// WHEN the higher priority media projection chip is added
@@ -222,17 +216,15 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
- fun chip_highestPriorityChipRemoved_showsNextPriorityChip() =
+ fun primaryChip_highestPriorityChipRemoved_showsNextPriorityChip() =
testScope.runTest {
// WHEN all chips are active
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- addDemoRonChip(commandRegistry, pw)
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
// THEN the highest priority screen record is used
assertIsScreenRecordChip(latest)
@@ -248,21 +240,15 @@
// THEN the lower priority call is used
assertIsCallChip(latest)
-
- // WHEN the higher priority call is removed
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
-
- // THEN the lower priority demo RON is used
- assertIsDemoRonChip(latest)
}
/** Regression test for b/347726238. */
@Test
- fun chip_timerDoesNotResetAfterSubscribersRestart() =
+ fun primaryChip_timerDoesNotResetAfterSubscribersRestart() =
testScope.runTest {
var latest: OngoingActivityChipModel? = null
- val job1 = underTest.chip.onEach { latest = it }.launchIn(this)
+ val job1 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
// Start a chip with a timer
systemClock.setElapsedRealtime(1234)
@@ -279,7 +265,7 @@
systemClock.setElapsedRealtime(5678)
// WHEN we re-subscribe to the chip flow
- val job2 = underTest.chip.onEach { latest = it }.launchIn(this)
+ val job2 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
runCurrent()
@@ -290,13 +276,13 @@
}
@Test
- fun chip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
+ fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value = MediaProjectionState.NotProjecting
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
assertIsScreenRecordChip(latest)
@@ -310,14 +296,14 @@
}
@Test
- fun chip_projectionStoppedViaDialog_chipHiddenWithoutAnimation() =
+ fun primaryChip_projectionStoppedViaDialog_chipHiddenWithoutAnimation() =
testScope.runTest {
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
screenRecordState.value = ScreenRecordModel.DoingNothing
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- val latest by collectLastValue(underTest.chip)
+ val latest by collectLastValue(underTest.primaryChip)
assertIsShareToAppChip(latest)
@@ -390,11 +376,5 @@
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
}
-
- fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) {
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
new file mode 100644
index 0000000..631120b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
@@ -0,0 +1,534 @@
+/*
+ * 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.statusbar.chips.ui.viewmodel
+
+import android.content.DialogInterface
+import android.content.packageManager
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+import android.platform.test.annotations.EnableFlags
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsCallChip
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog
+import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+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.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val systemClock = kosmos.fakeSystemClock
+ private val commandRegistry = kosmos.commandRegistry
+
+ private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
+ private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
+ private val callRepo = kosmos.ongoingCallRepository
+
+ private val pw = PrintWriter(StringWriter())
+
+ private val mockSystemUIDialog = mock<SystemUIDialog>()
+ private val chipBackgroundView = mock<ChipBackgroundContainer>()
+ private val chipView =
+ mock<View>().apply {
+ whenever(
+ this.requireViewById<ChipBackgroundContainer>(
+ R.id.ongoing_activity_chip_background
+ )
+ )
+ .thenReturn(chipBackgroundView)
+ }
+
+ private val underTest = kosmos.ongoingActivityChipsViewModel
+
+ @Before
+ fun setUp() {
+ setUpPackageManagerForMediaProjection(kosmos)
+ kosmos.demoRonChipViewModel.start()
+ val icon =
+ BitmapDrawable(
+ context.resources,
+ Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
+ )
+ whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
+ }
+
+ // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the
+ // flow has the right behavior to verify that we don't break any existing functionality.
+
+ @Test
+ fun primaryChip_allHidden_hidden() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun chips_allHidden_bothPrimaryAndSecondaryHidden() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ val latest by collectLastValue(underTest.chips)
+
+ assertThat(latest!!.primary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun primaryChip_screenRecordShow_restHidden_screenRecordShown() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertIsScreenRecordChip(latest)
+ }
+
+ @Test
+ fun chips_screenRecordShow_restHidden_primaryIsScreenRecordSecondaryIsHidden() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ val latest by collectLastValue(underTest.chips)
+
+ assertIsScreenRecordChip(latest!!.primary)
+ assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun primaryChip_screenRecordShowAndCallShow_screenRecordShown() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertIsScreenRecordChip(latest)
+ }
+
+ @Test
+ fun chips_screenRecordShowAndCallShow_primaryIsScreenRecordSecondaryIsCall() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ val latest by collectLastValue(underTest.chips)
+
+ assertIsScreenRecordChip(latest!!.primary)
+ assertIsCallChip(latest!!.secondary)
+ }
+
+ @Test
+ fun primaryChip_screenRecordShowAndShareToAppShow_screenRecordShown() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertIsScreenRecordChip(latest)
+ }
+
+ @Test
+ fun chips_screenRecordShowAndShareToAppShow_primaryIsScreenRecordSecondaryIsHidden() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ val latest by collectLastValue(underTest.chips)
+
+ assertIsScreenRecordChip(latest!!.primary)
+ // Even though share-to-app is active, we suppress it because this share-to-app is
+ // represented by screen record being active. See b/296461748.
+ assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun primaryChip_shareToAppShowAndCallShow_shareToAppShown() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertIsShareToAppChip(latest)
+ }
+
+ @Test
+ fun chips_shareToAppShowAndCallShow_primaryIsShareToAppSecondaryIsCall() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ val latest by collectLastValue(underTest.chips)
+
+ assertIsShareToAppChip(latest!!.primary)
+ assertIsCallChip(latest!!.secondary)
+ }
+
+ @Test
+ fun chips_threeActiveChips_topTwoShown() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ addDemoRonChip(commandRegistry, pw)
+
+ val latest by collectLastValue(underTest.chips)
+
+ assertIsScreenRecordChip(latest!!.primary)
+ assertIsCallChip(latest!!.secondary)
+ // Demo RON chip is dropped
+ }
+
+ @Test
+ fun primaryChip_onlyCallShown_callShown() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+ // MediaProjection covers both share-to-app and cast-to-other-device
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertIsCallChip(latest)
+ }
+
+ @Test
+ fun chips_onlyCallShown_primaryIsCallSecondaryIsHidden() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+ // MediaProjection covers both share-to-app and cast-to-other-device
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ val latest by collectLastValue(underTest.chips)
+
+ assertIsCallChip(latest!!.primary)
+ assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
+ testScope.runTest {
+ // Start with just the lowest priority chip shown
+ addDemoRonChip(commandRegistry, pw)
+ // And everything else hidden
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertIsDemoRonChip(latest)
+
+ // WHEN the higher priority call chip is added
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ // THEN the higher priority call chip is used
+ assertIsCallChip(latest)
+
+ // WHEN the higher priority media projection chip is added
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ NORMAL_PACKAGE,
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ // THEN the higher priority media projection chip is used
+ assertIsShareToAppChip(latest)
+
+ // WHEN the higher priority screen record chip is added
+ screenRecordState.value = ScreenRecordModel.Recording
+
+ // THEN the higher priority screen record chip is used
+ assertIsScreenRecordChip(latest)
+ }
+
+ @Test
+ fun primaryChip_highestPriorityChipRemoved_showsNextPriorityChip() =
+ testScope.runTest {
+ // WHEN all chips are active
+ screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ addDemoRonChip(commandRegistry, pw)
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ // THEN the highest priority screen record is used
+ assertIsScreenRecordChip(latest)
+
+ // WHEN the higher priority screen record is removed
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+
+ // THEN the lower priority media projection is used
+ assertIsShareToAppChip(latest)
+
+ // WHEN the higher priority media projection is removed
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+ // THEN the lower priority call is used
+ assertIsCallChip(latest)
+
+ // WHEN the higher priority call is removed
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ // THEN the lower priority demo RON is used
+ assertIsDemoRonChip(latest)
+ }
+
+ @Test
+ fun chips_movesChipsAroundAccordingToPriority() =
+ testScope.runTest {
+ // Start with just the lowest priority chip shown
+ addDemoRonChip(commandRegistry, pw)
+ // And everything else hidden
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+
+ val latest by collectLastValue(underTest.chips)
+
+ assertIsDemoRonChip(latest!!.primary)
+ assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+
+ // WHEN the higher priority call chip is added
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ // THEN the higher priority call chip is used as primary and demo ron is demoted to
+ // secondary
+ assertIsCallChip(latest!!.primary)
+ assertIsDemoRonChip(latest!!.secondary)
+
+ // WHEN the higher priority media projection chip is added
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ NORMAL_PACKAGE,
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ // THEN the higher priority media projection chip is used as primary and call is demoted
+ // to secondary (and demo RON is dropped altogether)
+ assertIsShareToAppChip(latest!!.primary)
+ assertIsCallChip(latest!!.secondary)
+
+ // WHEN the higher priority screen record chip is added
+ screenRecordState.value = ScreenRecordModel.Recording
+
+ // THEN the higher priority screen record chip is used
+ assertIsScreenRecordChip(latest!!.primary)
+
+ // WHEN screen record and call is dropped
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ // THEN media projection and demo RON remain
+ assertIsShareToAppChip(latest!!.primary)
+ assertIsDemoRonChip(latest!!.secondary)
+
+ // WHEN media projection is dropped
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+ // THEN demo RON is promoted to primary
+ assertIsDemoRonChip(latest!!.primary)
+ assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ /** Regression test for b/347726238. */
+ @Test
+ fun primaryChip_timerDoesNotResetAfterSubscribersRestart() =
+ testScope.runTest {
+ var latest: OngoingActivityChipModel? = null
+
+ val job1 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
+
+ // Start a chip with a timer
+ systemClock.setElapsedRealtime(1234)
+ screenRecordState.value = ScreenRecordModel.Recording
+
+ runCurrent()
+
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
+
+ // Stop subscribing to the chip flow
+ job1.cancel()
+
+ // Let time pass
+ systemClock.setElapsedRealtime(5678)
+
+ // WHEN we re-subscribe to the chip flow
+ val job2 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
+
+ runCurrent()
+
+ // THEN the old start time is still used
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
+
+ job2.cancel()
+ }
+
+ /** Regression test for b/347726238. */
+ @Test
+ fun chips_timerDoesNotResetAfterSubscribersRestart() =
+ testScope.runTest {
+ var latest: MultipleOngoingActivityChipsModel? = null
+
+ val job1 = underTest.chips.onEach { latest = it }.launchIn(this)
+
+ // Start a chip with a timer
+ systemClock.setElapsedRealtime(1234)
+ screenRecordState.value = ScreenRecordModel.Recording
+
+ runCurrent()
+
+ val primaryChip = latest!!.primary as OngoingActivityChipModel.Shown.Timer
+ assertThat(primaryChip.startTimeMs).isEqualTo(1234)
+
+ // Stop subscribing to the chip flow
+ job1.cancel()
+
+ // Let time pass
+ systemClock.setElapsedRealtime(5678)
+
+ // WHEN we re-subscribe to the chip flow
+ val job2 = underTest.chips.onEach { latest = it }.launchIn(this)
+
+ runCurrent()
+
+ // THEN the old start time is still used
+ val newPrimaryChip = latest!!.primary as OngoingActivityChipModel.Shown.Timer
+ assertThat(newPrimaryChip.startTimeMs).isEqualTo(1234)
+
+ job2.cancel()
+ }
+
+ @Test
+ fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
+ testScope.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionState.value = MediaProjectionState.NotProjecting
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertIsScreenRecordChip(latest)
+
+ // WHEN screen record gets stopped via dialog
+ val dialogStopAction =
+ getStopActionFromDialog(latest, chipView, mockSystemUIDialog, kosmos)
+ dialogStopAction.onClick(mock<DialogInterface>(), 0)
+
+ // THEN the chip is immediately hidden with no animation
+ assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false))
+ }
+
+ @Test
+ fun primaryChip_projectionStoppedViaDialog_chipHiddenWithoutAnimation() =
+ testScope.runTest {
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+ screenRecordState.value = ScreenRecordModel.DoingNothing
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ val latest by collectLastValue(underTest.primaryChip)
+
+ assertIsShareToAppChip(latest)
+
+ // WHEN media projection gets stopped via dialog
+ val dialogStopAction =
+ getStopActionFromDialog(latest, chipView, mockSystemUIDialog, kosmos)
+ dialogStopAction.onClick(mock<DialogInterface>(), 0)
+
+ // THEN the chip is immediately hidden with no animation
+ assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false))
+ }
+
+ private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) {
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
index 8cf7473..1efb3f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
@@ -40,6 +40,15 @@
}
@Test
+ fun parseColor() {
+ assertThat(Type.Color.parseValue("#434343").isSuccess).isTrue()
+ assertThat(Type.Color.parseValue("#aa123456").isSuccess).isTrue()
+ assertThat(Type.Color.parseValue("red").isSuccess).isTrue()
+
+ assertThat(Type.Color.parseValue("not a color").isFailure).isTrue()
+ }
+
+ @Test
fun mapToComplexType() {
val parseSquare = Type.Int.map { Rect(it, it, it, it) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index ed99705..b177e4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -101,7 +101,7 @@
private fun getAvalancheSuppressor() : AvalancheSuppressor {
return AvalancheSuppressor(
avalancheProvider, systemClock, settingsInteractor, packageManager,
- uiEventLogger, context, notificationManager, logger
+ uiEventLogger, context, notificationManager, logger, systemSettings
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index e4945fc..1a1af2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -342,7 +342,7 @@
val stackTop = 200f
val stackHeight = 800f
whenever(ambientState.stackTop).thenReturn(stackTop)
- whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
val shelfTop = stackTop + stackHeight - shelf.height
val stackScrollAlgorithmState = StackScrollAlgorithmState()
val viewInShelf = mock(ExpandableView::class.java)
@@ -378,7 +378,7 @@
val stackTop = 200f
val stackHeight = 800f
whenever(ambientState.stackTop).thenReturn(stackTop)
- whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
val paddingBetweenElements =
context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
whenever(ambientState.isShadeExpanded).thenReturn(true)
@@ -404,7 +404,7 @@
fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
- whenever(ambientState.stackHeight).thenReturn(100f)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(100f)
val paddingBetweenElements =
context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
val endOfStack = 200f + paddingBetweenElements
@@ -433,7 +433,7 @@
val stackTop = 200f
val stackHeight = 800f
whenever(ambientState.stackTop).thenReturn(stackTop)
- whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
val paddingBetweenElements =
context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
whenever(ambientState.isShadeExpanded).thenReturn(true)
@@ -459,7 +459,7 @@
fun updateState_withNullFirstViewInShelf_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
- whenever(ambientState.stackHeight).thenReturn(100f)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(100f)
val paddingBetweenElements =
context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
val endOfStack = 200f + paddingBetweenElements
@@ -488,7 +488,7 @@
val stackTop = 200f
val stackHeight = 800f
whenever(ambientState.stackTop).thenReturn(stackTop)
- whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
val paddingBetweenElements =
context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
val lastVisibleBackgroundChild = mock<ExpandableView>()
@@ -514,7 +514,7 @@
fun updateState_withCollapsedShade_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
- whenever(ambientState.stackHeight).thenReturn(100f)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(100f)
val paddingBetweenElements =
context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
val endOfStack = 200f + paddingBetweenElements
@@ -543,7 +543,7 @@
val stackTop = 200f
val stackHeight = 800f
whenever(ambientState.stackTop).thenReturn(stackTop)
- whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
val paddingBetweenElements =
context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
whenever(ambientState.isShadeExpanded).thenReturn(true)
@@ -583,7 +583,7 @@
fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
- whenever(ambientState.stackHeight).thenReturn(100f)
+ whenever(ambientState.interpolatedStackHeight).thenReturn(100f)
val paddingBetweenElements =
context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
val endOfStack = 200f + paddingBetweenElements
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 8d1228c..a06f4d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -95,6 +95,8 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -245,26 +247,12 @@
when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
.thenReturn((float) stackHeight);
- mStackScroller.updateContentHeight();
+ mStackScroller.updateStackHeight();
assertThat(mStackScroller.getIntrinsicStackHeight()).isEqualTo(stackHeight);
}
@Test
- @DisableSceneContainer
- public void testIntrinsicStackHeight_includesTopScrimPadding() {
- int stackHeight = 300;
- int topScrimPadding = px(R.dimen.notification_side_paddings);
- when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
- .thenReturn((float) stackHeight);
-
- mStackScroller.updateContentHeight();
-
- assertThat(mStackScroller.getIntrinsicStackHeight())
- .isEqualTo(stackHeight + topScrimPadding);
- }
-
- @Test
@DisableSceneContainer // TODO(b/312473478): address disabled test
public void testUpdateStackHeight_qsExpansionZero() {
final float expansionFraction = 0.2f;
@@ -289,8 +277,8 @@
endHeight * StackScrollAlgorithm.START_FRACTION,
endHeight, expansionFraction);
- mStackScroller.updateStackHeight(endHeight, expansionFraction);
- assertThat(mAmbientState.getStackHeight()).isEqualTo(expected);
+ mStackScroller.updateInterpolatedStackHeight(endHeight, expansionFraction);
+ assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(expected);
}
@Test
@@ -309,7 +297,7 @@
// THEN stackHeight and stackEndHeight are the same
verify(mAmbientState).setStackEndHeight(stackEndHeight);
- verify(mAmbientState).setStackHeight(stackEndHeight);
+ verify(mAmbientState).setInterpolatedStackHeight(stackEndHeight);
}
@Test
@@ -329,7 +317,7 @@
// THEN stackHeight is changed by the expansion frac
verify(mAmbientState).setStackEndHeight(stackEndHeight);
- verify(mAmbientState).setStackHeight(stackEndHeight * 0.75f);
+ verify(mAmbientState).setInterpolatedStackHeight(stackEndHeight * 0.75f);
}
@Test
@@ -349,7 +337,7 @@
// THEN stackHeight is measured from the stack top
verify(mAmbientState).setStackEndHeight(stackEndHeight);
- verify(mAmbientState).setStackHeight(stackEndHeight);
+ verify(mAmbientState).setInterpolatedStackHeight(stackEndHeight);
}
@Test
@@ -363,7 +351,7 @@
clearInvocations(mAmbientState);
mStackScroller.updateStackEndHeightAndStackHeight(1f);
- verify(mAmbientState).setStackHeight(eq(300f));
+ verify(mAmbientState).setInterpolatedStackHeight(eq(300f));
}
@Test
@@ -376,7 +364,7 @@
clearInvocations(mAmbientState);
mStackScroller.updateStackEndHeightAndStackHeight(expansionFraction);
verify(mAmbientState, never()).setStackEndHeight(anyFloat());
- verify(mAmbientState).setStackHeight(anyFloat());
+ verify(mAmbientState).setInterpolatedStackHeight(anyFloat());
}
@Test
@@ -391,14 +379,14 @@
clearInvocations(mAmbientState);
mStackScroller.updateStackEndHeightAndStackHeight(expansionFraction);
verify(mAmbientState, never()).setStackEndHeight(anyFloat());
- verify(mAmbientState).setStackHeight(anyFloat());
+ verify(mAmbientState).setInterpolatedStackHeight(anyFloat());
// Validate that when the animation ends the stackEndHeight is recalculated immediately
clearInvocations(mAmbientState);
mStackScroller.setPanelFlinging(false);
verify(mAmbientState).setFlinging(eq(false));
verify(mAmbientState).setStackEndHeight(anyFloat());
- verify(mAmbientState).setStackHeight(anyFloat());
+ verify(mAmbientState).setInterpolatedStackHeight(anyFloat());
}
@Test
@@ -440,6 +428,86 @@
}
@Test
+ @EnableSceneContainer
+ public void setExpandFraction_fullyCollapsed() {
+ // Given: NSSL has a height
+ when(mStackScroller.getHeight()).thenReturn(1200);
+ // And: stack bounds are set
+ float expandFraction = 0.0f;
+ float stackTop = 100;
+ float stackCutoff = 1100;
+ float stackHeight = stackCutoff - stackTop;
+ mStackScroller.setStackTop(stackTop);
+ mStackScroller.setStackCutoff(stackCutoff);
+
+ // When: panel is fully collapsed
+ mStackScroller.setExpandFraction(expandFraction);
+
+ // Then
+ assertThat(mAmbientState.getExpansionFraction()).isEqualTo(expandFraction);
+ assertThat(mAmbientState.isExpansionChanging()).isFalse();
+ assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight);
+ assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(
+ stackHeight * StackScrollAlgorithm.START_FRACTION);
+ assertThat(mAmbientState.isShadeExpanded()).isFalse();
+ assertThat(mStackScroller.getExpandedHeight()).isZero();
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void setExpandFraction_expanding() {
+ // Given: NSSL has a height
+ when(mStackScroller.getHeight()).thenReturn(1200);
+ // And: stack bounds are set
+ float expandFraction = 0.6f;
+ float stackTop = 100;
+ float stackCutoff = 1100;
+ float stackHeight = stackCutoff - stackTop;
+ mStackScroller.setStackTop(stackTop);
+ mStackScroller.setStackCutoff(stackCutoff);
+
+ // When: panel is expanding
+ mStackScroller.setExpandFraction(expandFraction);
+
+ // Then
+ assertThat(mAmbientState.getExpansionFraction()).isEqualTo(expandFraction);
+ assertThat(mAmbientState.isExpansionChanging()).isTrue();
+ assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight);
+ assertThat(mAmbientState.getInterpolatedStackHeight()).isGreaterThan(
+ stackHeight * StackScrollAlgorithm.START_FRACTION);
+ assertThat(mAmbientState.getInterpolatedStackHeight()).isLessThan(stackHeight);
+ assertThat(mStackScroller.getExpandedHeight()).isGreaterThan(0f);
+ assertThat(mAmbientState.isShadeExpanded()).isTrue();
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void setExpandFraction_fullyExpanded() {
+ // Given: NSSL has a height
+ int viewHeight = 1200;
+ when(mStackScroller.getHeight()).thenReturn(viewHeight);
+ // And: stack bounds are set
+ float expandFraction = 1.0f;
+ float stackTop = 100;
+ float stackCutoff = 1100;
+ float stackHeight = stackCutoff - stackTop;
+ mStackScroller.setStackTop(stackTop);
+ mStackScroller.setStackCutoff(stackCutoff);
+
+ // When: panel is fully expanded
+ mStackScroller.setExpandFraction(expandFraction);
+
+ // Then
+ assertThat(mAmbientState.getExpansionFraction()).isEqualTo(expandFraction);
+ assertThat(mAmbientState.isExpansionChanging()).isFalse();
+ assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight);
+ assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(stackHeight);
+ assertThat(mStackScroller.getExpandedHeight()).isEqualTo(viewHeight);
+ assertThat(mAmbientState.isShadeExpanded()).isTrue();
+ }
+
+ @Test
+ @DisableSceneContainer
public void testSetExpandedHeight_listenerReceivedCallbacks() {
final float expectedHeight = 0f;
@@ -466,6 +534,7 @@
}
@Test
+ @DisableSceneContainer
public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() {
mTestableResources
.addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
@@ -826,7 +895,7 @@
@Test
@DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
- @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer
public void testInsideQSHeader_noOffset() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
@@ -844,7 +913,7 @@
@Test
@DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
- @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer
public void testInsideQSHeader_Offset() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
@@ -865,7 +934,7 @@
@Test
@EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
- @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer
public void testInsideQSHeader_noOffset_qsCompose() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
@@ -892,7 +961,7 @@
@Test
@EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
- @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer
public void testInsideQSHeader_Offset_qsCompose() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
@@ -921,6 +990,53 @@
}
@Test
+ @EnableSceneContainer
+ public void testIsInsideScrollableRegion_noScrim() {
+ mStackScroller.setLeftTopRightBottom(0, 0, 2000, 2000);
+
+ MotionEvent event = transformEventForView(createMotionEvent(250f, 250f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event)).isTrue();
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void testIsInsideScrollableRegion_noOffset() {
+ mStackScroller.setLeftTopRightBottom(0, 0, 1000, 2000);
+ mStackScroller.setScrimClippingShape(createScrimShape(100, 500, 900, 2000));
+
+ MotionEvent event1 = transformEventForView(createMotionEvent(500f, 400f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event1)).isFalse();
+
+ MotionEvent event2 = transformEventForView(createMotionEvent(50, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event2)).isFalse();
+
+ MotionEvent event3 = transformEventForView(createMotionEvent(950f, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event3)).isFalse();
+
+ MotionEvent event4 = transformEventForView(createMotionEvent(500f, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event4)).isTrue();
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void testIsInsideScrollableRegion_offset() {
+ mStackScroller.setLeftTopRightBottom(1000, 0, 2000, 2000);
+ mStackScroller.setScrimClippingShape(createScrimShape(100, 500, 900, 2000));
+
+ MotionEvent event1 = transformEventForView(createMotionEvent(1500f, 400f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event1)).isFalse();
+
+ MotionEvent event2 = transformEventForView(createMotionEvent(1050, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event2)).isFalse();
+
+ MotionEvent event3 = transformEventForView(createMotionEvent(1950f, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event3)).isFalse();
+
+ MotionEvent event4 = transformEventForView(createMotionEvent(1500f, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event4)).isTrue();
+ }
+
+ @Test
@DisableSceneContainer // TODO(b/312473478): address disabled test
public void setFractionToShade_recomputesStackHeight() {
mStackScroller.setFractionToShade(1f);
@@ -1371,7 +1487,7 @@
private static MotionEvent transformEventForView(MotionEvent event, View view) {
// From `ViewGroup#dispatchTransformedTouchEvent`
MotionEvent transformed = event.copy();
- transformed.offsetLocation(-view.getTop(), -view.getLeft());
+ transformed.offsetLocation(/* deltaX = */-view.getLeft(), /* deltaY = */ -view.getTop());
return transformed;
}
@@ -1407,4 +1523,9 @@
}
private abstract static class BooleanConsumer implements Consumer<Boolean> { }
+
+ private ShadeScrimShape createScrimShape(int left, int top, int right, int bottom) {
+ ShadeScrimBounds bounds = new ShadeScrimBounds(left, top, right, bottom);
+ return new ShadeScrimShape(bounds, 0, 0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 7e79019..3e8bf47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -1114,6 +1114,7 @@
}
@Test
+ @DisableSceneContainer
fun shadeOpened_hunDoesNotOverlapQQS_hunShouldHaveNoShadow() {
// Given: shade is opened, yTranslation of HUN is equal to QQS Panel's height,
// the height of HUN is equal to the height of QQS Panel,
@@ -1144,6 +1145,7 @@
}
@Test
+ @DisableSceneContainer
fun shadeClosed_hunShouldHaveFullShadow() {
// Given: shade is closed, ambientState.stackTranslation == -ambientState.topPadding,
// the height of HUN is equal to the height of QQS Panel,
@@ -1172,6 +1174,7 @@
}
@Test
+ @DisableSceneContainer
fun draggingHunToOpenShade_hunShouldHavePartialShadow() {
// Given: shade is closed when HUN pops up,
// now drags down the HUN to open shade
@@ -1447,7 +1450,7 @@
// set stackEndHeight and stackHeight
// ExpansionFractionWithoutShelf == stackHeight / stackEndHeight
ambientState.stackEndHeight = 100f
- ambientState.stackHeight = ambientState.stackEndHeight * fraction
+ ambientState.interpolatedStackHeight = ambientState.stackEndHeight * fraction
}
private fun resetViewStates_hunYTranslationIs(expected: Float) {
@@ -1531,7 +1534,7 @@
// shade is fully open
ambientState.expansionFraction = 1.0f
with(fullStackHeight) {
- ambientState.stackHeight = this
+ ambientState.interpolatedStackHeight = this
ambientState.stackEndHeight = this
}
stackScrollAlgorithm.setIsExpanded(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
index 2f81027..c7919df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
@@ -20,6 +20,7 @@
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.FakeExecutor
@@ -35,6 +36,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.never
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -46,12 +48,20 @@
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- controller = ManagedProfileControllerImpl(context, mainExecutor, userTracker, userManager)
+ controller =
+ ManagedProfileControllerImpl(
+ context,
+ mainExecutor,
+ userTracker,
+ userManager,
+ keyguardUpdateMonitor
+ )
}
@Test
@@ -107,6 +117,24 @@
captor.value.onProfilesChanged(userManager.getEnabledProfiles(1))
}
+ @Test
+ fun hasWorkingProfile_setWorkModeEnabled_callsAwakenFromDream() {
+ `when`(userTracker.userId).thenReturn(1)
+ setupWorkingProfile(1)
+ `when`(userManager.requestQuietModeEnabled(any(), any())).thenReturn(false)
+ controller.hasActiveProfile()
+
+ controller.isWorkModeEnabled = true
+
+ verify(keyguardUpdateMonitor).awakenFromDream()
+ }
+
+ @Test
+ fun noWorkingProfile_setWorkModeEnabled_NoAwakenFromDreamCall() {
+ controller.isWorkModeEnabled = true
+ verify(keyguardUpdateMonitor, never()).awakenFromDream()
+ }
+
private fun setupWorkingProfile(userId: Int) {
`when`(userManager.getEnabledProfiles(userId))
.thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index 76dc65c..2ed3473 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -34,6 +34,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.StatusBarIcon
import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
@@ -41,6 +42,7 @@
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
+import com.android.systemui.kosmos.testScope
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.screenrecord.RecordingController
@@ -71,9 +73,7 @@
import com.android.systemui.util.time.FakeSystemClock
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -145,7 +145,7 @@
private lateinit var alarmCallbackCaptor:
ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback>
- private val testScope = TestScope(UnconfinedTestDispatcher())
+ private val testScope = kosmos.testScope
private val fakeConnectedDisplayStateProvider = FakeConnectedDisplayStateProvider()
private val zenModeController = FakeZenModeController()
@@ -249,7 +249,7 @@
statusBarPolicy.init()
clearInvocations(iconController)
- fakeConnectedDisplayStateProvider.emit(State.CONNECTED)
+ fakeConnectedDisplayStateProvider.setState(State.CONNECTED)
runCurrent()
verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true)
@@ -261,7 +261,8 @@
statusBarPolicy.init()
clearInvocations(iconController)
- fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED)
+ fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED)
+ runCurrent()
verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, false)
}
@@ -272,9 +273,12 @@
statusBarPolicy.init()
clearInvocations(iconController)
- fakeConnectedDisplayStateProvider.emit(State.CONNECTED)
- fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED)
- fakeConnectedDisplayStateProvider.emit(State.CONNECTED)
+ fakeConnectedDisplayStateProvider.setState(State.CONNECTED)
+ runCurrent()
+ fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED)
+ runCurrent()
+ fakeConnectedDisplayStateProvider.setState(State.CONNECTED)
+ runCurrent()
inOrder(iconController).apply {
verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true)
@@ -289,7 +293,8 @@
statusBarPolicy.init()
clearInvocations(iconController)
- fakeConnectedDisplayStateProvider.emit(State.CONNECTED_SECURE)
+ fakeConnectedDisplayStateProvider.setState(State.CONNECTED_SECURE)
+ runCurrent()
verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true)
}
@@ -390,7 +395,7 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS)
+ @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS)
fun zenModeInteractorActiveModeChanged_showsModeIcon() =
testScope.runTest {
statusBarPolicy.init()
@@ -403,8 +408,8 @@
.setName("Bedtime Mode")
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setActive(true)
- .setPackage("some.package")
- .setIconResId(123)
+ .setPackage(mContext.packageName)
+ .setIconResId(android.R.drawable.ic_lock_lock)
.build(),
TestModeBuilder()
.setId("other")
@@ -412,7 +417,7 @@
.setType(AutomaticZenRule.TYPE_OTHER)
.setActive(true)
.setPackage(SystemZenRules.PACKAGE_ANDROID)
- .setIconResId(456)
+ .setIconResId(android.R.drawable.ic_media_play)
.build(),
)
)
@@ -422,17 +427,25 @@
verify(iconController)
.setResourceIcon(
eq(ZEN_SLOT),
- eq("some.package"),
- eq(123),
- eq(null),
- eq("Bedtime Mode")
+ eq(mContext.packageName),
+ eq(android.R.drawable.ic_lock_lock),
+ any(), // non-null
+ eq("Bedtime Mode"),
+ eq(StatusBarIcon.Shape.FIXED_SPACE)
)
zenModeRepository.deactivateMode("bedtime")
runCurrent()
verify(iconController)
- .setResourceIcon(eq(ZEN_SLOT), eq(null), eq(456), eq(null), eq("Other Mode"))
+ .setResourceIcon(
+ eq(ZEN_SLOT),
+ eq(null),
+ eq(android.R.drawable.ic_media_play),
+ any(), // non-null
+ eq("Other Mode"),
+ eq(StatusBarIcon.Shape.FIXED_SPACE)
+ )
zenModeRepository.deactivateMode("other")
runCurrent()
@@ -441,7 +454,7 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS)
+ @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS)
fun zenModeControllerOnGlobalZenChanged_doesNotUpdateDndIcon() {
statusBarPolicy.init()
reset(iconController)
@@ -450,7 +463,8 @@
verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any())
verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any())
- verify(iconController, never()).setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any())
+ verify(iconController, never())
+ .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any(), any())
}
@Test
@@ -466,7 +480,7 @@
verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any())
verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any())
verify(iconController, never())
- .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any())
+ .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any(), any())
}
@Test
@@ -529,9 +543,11 @@
}
private class FakeConnectedDisplayStateProvider : ConnectedDisplayInteractor {
- private val flow = MutableSharedFlow<State>()
+ private val flow = MutableStateFlow(State.DISCONNECTED)
- suspend fun emit(value: State) = flow.emit(value)
+ fun setState(value: State) {
+ flow.value = value
+ }
override val connectedDisplayState: Flow<State>
get() = flow
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 3e3c046..1d74331 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -779,6 +779,26 @@
@Test
@DisableSceneContainer
+ public void testResetDoesNotHideBouncerWhenNotShowing() {
+ reset(mDismissCallbackRegistry);
+ reset(mPrimaryBouncerInteractor);
+
+ // GIVEN the keyguard is showing
+ reset(mAlternateBouncerInteractor);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false);
+
+ // WHEN SBKV is reset with hideBouncerWhenShowing=true
+ mStatusBarKeyguardViewManager.reset(true);
+
+ // THEN no calls to hide should be made
+ verify(mAlternateBouncerInteractor, never()).hide();
+ verify(mDismissCallbackRegistry, never()).notifyDismissCancelled();
+ verify(mPrimaryBouncerInteractor, never()).setDismissAction(eq(null), eq(null));
+ }
+
+ @Test
+ @DisableSceneContainer
public void testResetHideBouncerWhenShowing_alternateBouncerHides() {
reset(mDismissCallbackRegistry);
reset(mPrimaryBouncerInteractor);
@@ -786,6 +806,7 @@
// GIVEN the keyguard is showing
reset(mAlternateBouncerInteractor);
when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
// WHEN SBKV is reset with hideBouncerWhenShowing=true
mStatusBarKeyguardViewManager.reset(true);
@@ -1091,9 +1112,11 @@
public void testShowBouncerOrKeyguard_showsKeyguardIfShowBouncerReturnsFalse() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
+ // Returning false means unable to show the bouncer
when(mPrimaryBouncerInteractor.show(true)).thenReturn(false);
when(mKeyguardTransitionInteractor.getTransitionState().getValue().getTo())
.thenReturn(KeyguardState.LOCKSCREEN);
+ mStatusBarKeyguardViewManager.onStartedWakingUp();
reset(mCentralSurfaces);
// Advance past reattempts
@@ -1106,6 +1129,23 @@
@Test
@DisableSceneContainer
+ @EnableFlags(Flags.FLAG_SIM_PIN_RACE_CONDITION_ON_RESTART)
+ public void testShowBouncerOrKeyguard_showsKeyguardIfSleeping() {
+ when(mKeyguardTransitionInteractor.getTransitionState().getValue().getTo())
+ .thenReturn(KeyguardState.LOCKSCREEN);
+ mStatusBarKeyguardViewManager.onStartedGoingToSleep();
+
+ reset(mCentralSurfaces);
+ reset(mPrimaryBouncerInteractor);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(
+ /* hideBouncerWhenShowing= */true, false);
+ verify(mCentralSurfaces).showKeyguard();
+ verify(mPrimaryBouncerInteractor).hide();
+ }
+
+
+ @Test
+ @DisableSceneContainer
public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing() {
boolean isFalsingReset = false;
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
index 219b16f..d7fb129 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
@@ -34,12 +34,13 @@
@RunWith(AndroidJUnit4::class)
class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() {
- private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- private val disableFlagsLogger = DisableFlagsLogger(
+ private val buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java)).create("buffer", 10)
+ private val disableFlagsLogger =
+ DisableFlagsLogger(
listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
- )
+ )
private val logger = CollapsedStatusBarFragmentLogger(buffer, disableFlagsLogger)
@Test
@@ -66,7 +67,8 @@
StatusBarVisibilityModel(
showClock = false,
showNotificationIcons = true,
- showOngoingActivityChip = false,
+ showPrimaryOngoingActivityChip = false,
+ showSecondaryOngoingActivityChip = false,
showSystemInfo = true,
)
)
@@ -77,7 +79,8 @@
assertThat(actualString).contains("showClock=false")
assertThat(actualString).contains("showNotificationIcons=true")
- assertThat(actualString).contains("showOngoingActivityChip=false")
+ assertThat(actualString).contains("showPrimaryOngoingActivityChip=false")
+ assertThat(actualString).contains("showSecondaryOngoingActivityChip=false")
assertThat(actualString).contains("showSystemInfo=true")
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index bea027f..135fab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,6 +16,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS;
import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
@@ -432,8 +433,7 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
@Test
@@ -445,8 +445,7 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@@ -460,8 +459,7 @@
fragment.disable(DEFAULT_DISPLAY,
StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
@Test
@@ -474,8 +472,7 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
@Test
@@ -487,22 +484,19 @@
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
// Ongoing call ended
when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
// Ongoing call started
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
}
@Test
@@ -533,23 +527,26 @@
// WHEN there's *no* ongoing activity via new callback
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ false, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ false,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
// THEN the old callback value is used, so the view is shown
- assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
// WHEN there's *no* ongoing call via old callback
when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- // WHEN there *is* an ongoing activity via new callback
+ // WHEN there *are* ongoing activities via new callback
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ true,
+ /* shouldAnimate= */ false);
- // THEN the old callback value is used, so the view is hidden
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ // THEN the old callback value is used, so the views are hidden
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
+ assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
@Test
@@ -562,85 +559,221 @@
// listener, but I'm unable to get the fragment to get attached so that the binder starts
// listening to flows.
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ false, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ false,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- public void hasOngoingActivity_chipDisplayedAndNotificationIconsHidden() {
+ public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() {
resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
- assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- public void hasOngoingActivityButNotificationIconsDisabled_chipHidden() {
+ @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() {
+ resumeAndGetFragment();
+
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ true,
+ /* shouldAnimate= */ false);
+
+ assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
+ }
+
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
+ resumeAndGetFragment();
+
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ true,
+ /* shouldAnimate= */ false);
+
+ assertEquals(View.VISIBLE, getSecondaryOngoingActivityChipView().getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
fragment.disable(DEFAULT_DISPLAY,
StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
+ }
+
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ true,
+ /* shouldAnimate= */ false);
+
+ fragment.disable(DEFAULT_DISPLAY,
+ StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
+ assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- public void hasOngoingActivityButAlsoHun_chipHidden() {
+ @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
+ }
+
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ true,
+ /* shouldAnimate= */ false);
+ when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
+ assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- public void ongoingActivityEnded_chipHidden() {
+ @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() {
resumeAndGetFragment();
// Ongoing activity started
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
- assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
// Ongoing activity ended
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ false, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ false,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
+ }
+
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() {
+ resumeAndGetFragment();
+
+ // Ongoing activity started
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
+
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
+
+ // Ongoing activity ended
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ false,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
+
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
+ }
+
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ public void secondaryOngoingActivityEnded_chipHidden() {
+ resumeAndGetFragment();
+
+ // Secondary ongoing activity started
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ true,
+ /* shouldAnimate= */ false);
+
+ assertEquals(View.VISIBLE, getSecondaryOngoingActivityChipView().getVisibility());
+
+ // Ongoing activity ended
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
+
+ assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- public void hasOngoingActivity_hidesNotifsWithoutAnimation() {
+ @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
fragment.enableAnimationsForTesting();
- // Ongoing call started
+ // Ongoing activity started
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
+
+ // Notification area is hidden without delay
+ assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01);
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
+ }
+
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ // Enable animations for testing so that we can verify we still aren't animating
+ fragment.enableAnimationsForTesting();
+
+ // Ongoing activity started
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
// Notification area is hidden without delay
assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01);
@@ -649,7 +782,8 @@
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- public void screenSharingChipsEnabled_ignoresOngoingCallController() {
+ @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
@@ -658,23 +792,58 @@
// WHEN there's *no* ongoing activity via new callback
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ false, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ false,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
// THEN the new callback value is used, so the view is hidden
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
// WHEN there's *no* ongoing call via old callback
when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- // WHEN there *is* an ongoing activity via new callback
+ // WHEN there *are* ongoing activities via new callback
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
- // THEN the new callback value is used, so the view is shown
- assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+ // THEN the new callback value is used, so the views are shown
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
+ }
+
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+ // WHEN there *is* an ongoing call via old callback
+ when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
+
+ // WHEN there's *no* ongoing activity via new callback
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ false,
+ /* hasSecondaryOngoingActivity= */ false,
+ /* shouldAnimate= */ false);
+
+ // THEN the new callback value is used, so the view is hidden
+ assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
+
+ // WHEN there's *no* ongoing call via old callback
+ when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ // WHEN there *are* ongoing activities via new callback
+ mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+ /* hasPrimaryOngoingActivity= */ true,
+ /* hasSecondaryOngoingActivity= */ true,
+ /* shouldAnimate= */ false);
+
+ // THEN the new callback value is used, so the views are shown
+ assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
+ assertEquals(View.VISIBLE, getSecondaryOngoingActivityChipView().getVisibility());
}
@Test
@@ -1023,4 +1192,12 @@
private View getNotificationAreaView() {
return mFragment.getView().findViewById(R.id.notificationIcons);
}
+
+ private View getPrimaryOngoingActivityChipView() {
+ return mFragment.getView().findViewById(R.id.ongoing_activity_chip_primary);
+ }
+
+ private View getSecondaryOngoingActivityChipView() {
+ return mFragment.getView().findViewById(R.id.ongoing_activity_chip_secondary);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
index 9f6f51a..d47a903 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
@@ -39,7 +39,8 @@
StatusBarVisibilityModel(
showClock = true,
showNotificationIcons = true,
- showOngoingActivityChip = true,
+ showPrimaryOngoingActivityChip = true,
+ showSecondaryOngoingActivityChip = true,
showSystemInfo = true,
)
@@ -75,17 +76,19 @@
}
@Test
- fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingActivityChipTrue() {
+ fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingActivityChipsTrue() {
val result = createModelFromFlags(disabled1 = 0, disabled2 = 0)
- assertThat(result.showOngoingActivityChip).isTrue()
+ assertThat(result.showPrimaryOngoingActivityChip).isTrue()
+ assertThat(result.showSecondaryOngoingActivityChip).isTrue()
}
@Test
- fun createModelFromFlags_ongoingCallChipDisabled_showOngoingActivityChipFalse() {
+ fun createModelFromFlags_ongoingCallChipDisabled_showOngoingActivityChipsFalse() {
val result = createModelFromFlags(disabled1 = DISABLE_ONGOING_CALL_CHIP, disabled2 = 0)
- assertThat(result.showOngoingActivityChip).isFalse()
+ assertThat(result.showPrimaryOngoingActivityChip).isFalse()
+ assertThat(result.showSecondaryOngoingActivityChip).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
new file mode 100644
index 0000000..90732d01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.statusbar.phone.ui
+
+import android.app.Flags
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
+import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter
+import com.android.systemui.util.Assert
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.RETURNS_DEEP_STUBS
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class IconManagerTest : SysuiTestCase() {
+
+ private lateinit var underTest: IconManager
+ private lateinit var viewGroup: ViewGroup
+
+ @Before
+ fun setUp() {
+ Assert.setTestThread(Thread.currentThread())
+ viewGroup = LinearLayout(context)
+ underTest =
+ IconManager(
+ viewGroup,
+ StatusBarLocation.HOME,
+ mock<WifiUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS),
+ mock<MobileUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS),
+ mock<MobileContextProvider>(defaultAnswer = RETURNS_DEEP_STUBS),
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+ fun addIcon_shapeWrapContent_addsIconViewWithVariableWidth() {
+ val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.WRAP_CONTENT)
+
+ underTest.addIcon(0, "slot", false, sbIcon)
+
+ assertThat(viewGroup.childCount).isEqualTo(1)
+ val iconView = viewGroup.getChildAt(0) as StatusBarIconView
+ assertThat(iconView).isNotNull()
+
+ assertThat(iconView.layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+ assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.CENTER)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+ fun addIcon_shapeFixedSpace_addsIconViewWithFixedWidth() {
+ val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.FIXED_SPACE)
+
+ underTest.addIcon(0, "slot", false, sbIcon)
+
+ assertThat(viewGroup.childCount).isEqualTo(1)
+ val iconView = viewGroup.getChildAt(0) as StatusBarIconView
+ assertThat(iconView).isNotNull()
+
+ assertThat(iconView.layoutParams.width).isNotEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+ assertThat(iconView.layoutParams.width).isEqualTo(iconView.layoutParams.height)
+ assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.FIT_CENTER)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MODES_UI_ICONS)
+ fun addIcon_iconsFlagOff_addsIconViewWithVariableWidth() {
+ val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.FIXED_SPACE)
+
+ underTest.addIcon(0, "slot", false, sbIcon)
+
+ assertThat(viewGroup.childCount).isEqualTo(1)
+ val iconView = viewGroup.getChildAt(0) as StatusBarIconView
+ assertThat(iconView).isNotNull()
+
+ assertThat(iconView.layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+ assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.CENTER)
+ }
+
+ private fun newStatusBarIcon(shape: StatusBarIcon.Shape) =
+ StatusBarIcon(
+ UserHandle.CURRENT,
+ context.packageName,
+ Icon.createWithResource(context, android.R.drawable.ic_media_next),
+ 0,
+ 0,
+ "",
+ StatusBarIcon.Type.ResourceIcon,
+ shape,
+ )
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
index 26a57e4..50a13b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
@@ -424,7 +424,14 @@
@EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS)
fun setResourceIcon_setsIconAndPreloadedIconInHolder() {
val drawable = ColorDrawable(1)
- underTest.setResourceIcon("slot", "some.package", 123, drawable, "description")
+ underTest.setResourceIcon(
+ "slot",
+ "some.package",
+ 123,
+ drawable,
+ "description",
+ StatusBarIcon.Shape.FIXED_SPACE
+ )
val iconHolder = iconList.getIconHolder("slot", 0)
assertThat(iconHolder).isNotNull()
@@ -432,6 +439,7 @@
assertThat(iconHolder?.icon?.icon?.resId).isEqualTo(123)
assertThat(iconHolder?.icon?.icon?.resPackage).isEqualTo("some.package")
assertThat(iconHolder?.icon?.contentDescription).isEqualTo("description")
+ assertThat(iconHolder?.icon?.shape).isEqualTo(StatusBarIcon.Shape.FIXED_SPACE)
assertThat(iconHolder?.icon?.preloadedIcon).isEqualTo(drawable)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
index db3e533..7901f47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
@@ -19,13 +19,11 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -39,9 +37,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class AirplaneModeInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
- private val mobileConnectionsRepository =
- FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), mock<TableLogBuffer> {})
+ private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
private val airplaneModeRepository = FakeAirplaneModeRepository()
private val connectivityRepository = FakeConnectivityRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index b823333..8beed01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -19,12 +19,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -35,7 +36,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
@@ -43,10 +43,11 @@
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@RunWith(AndroidJUnit4::class)
class AirplaneModeViewModelImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private lateinit var underTest: AirplaneModeViewModelImpl
- @Mock private lateinit var logger: TableLogBuffer
+ private val logger = logcatTableLogBuffer(kosmos, "AirplaneModeViewModelImplTest")
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
private lateinit var connectivityRepository: FakeConnectivityRepository
private lateinit var interactor: AirplaneModeInteractor
@@ -61,7 +62,7 @@
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
- FakeMobileConnectionsRepository(),
+ kosmos.fakeMobileConnectionsRepository,
)
scope = CoroutineScope(IMMEDIATE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 7d586cd..36f5236 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -27,7 +27,7 @@
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.tableLogBufferFactory
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -42,11 +42,11 @@
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -56,7 +56,6 @@
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -75,12 +74,13 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileRepositorySwitcherTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var underTest: MobileRepositorySwitcher
private lateinit var realRepo: MobileConnectionsRepositoryImpl
private lateinit var demoRepo: DemoMobileConnectionsRepository
private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
private lateinit var wifiDataSource: DemoModeWifiDataSource
- private lateinit var logFactory: TableLogBufferFactory
private lateinit var wifiRepository: FakeWifiRepository
private lateinit var connectivityRepository: ConnectivityRepository
@@ -95,16 +95,12 @@
private val mobileMappings = FakeMobileMappingsProxy()
private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
- private val testDispatcher = UnconfinedTestDispatcher()
private val scope = CoroutineScope(IMMEDIATE)
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- logFactory =
- TableLogBufferFactory(dumpManager, FakeSystemClock(), mock(), testDispatcher, scope)
-
// Never start in demo mode
whenever(demoModeController.isInDemoMode).thenReturn(false)
@@ -147,7 +143,7 @@
wifiDataSource = wifiDataSource,
scope = scope,
context = context,
- logFactory = logFactory,
+ logFactory = kosmos.tableLogBufferFactory,
)
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index db6f5927..7d32021 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -23,16 +23,16 @@
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.tableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
@@ -43,12 +43,11 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.After
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-import platform.test.runner.parameterized.Parameter
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
/**
* Parameterized test for all of the common values of [FakeNetworkEventModel]. This test simply
@@ -60,19 +59,11 @@
@RunWith(ParameterizedAndroidJunit4::class)
internal class DemoMobileConnectionParameterizedTest(private val testCase: TestCase) :
SysuiTestCase() {
+ private val kosmos = testKosmos()
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
- private val logFactory =
- TableLogBufferFactory(
- mock(),
- FakeSystemClock(),
- mock(),
- testDispatcher,
- testScope.backgroundScope,
- )
-
private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
@@ -99,7 +90,7 @@
wifiDataSource = mockWifiDataSource,
scope = testScope.backgroundScope,
context = context,
- logFactory = logFactory,
+ logFactory = kosmos.tableLogBufferFactory,
)
connectionsRepo.startProcessingCommands()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index 5e0d2fb..5017dda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -24,8 +24,7 @@
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.tableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -34,9 +33,9 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -56,21 +55,13 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
- private val dumpManager: DumpManager = mock()
+ private val kosmos = testKosmos()
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
- private val logFactory =
- TableLogBufferFactory(
- dumpManager,
- FakeSystemClock(),
- mock(),
- testDispatcher,
- testScope.backgroundScope,
- )
private lateinit var underTest: DemoMobileConnectionsRepository
private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
@@ -94,7 +85,7 @@
wifiDataSource = wifiDataSource,
scope = testScope.backgroundScope,
context = context,
- logFactory = logFactory,
+ logFactory = kosmos.tableLogBufferFactory,
)
underTest.startProcessingCommands()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
index 237aabc..715e3b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -85,7 +85,7 @@
underTest.dataConnectionState.onEach { latestConnState = it }.launchIn(this)
val netJob = underTest.resolvedNetworkType.onEach { latestNetType = it }.launchIn(this)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive())
assertThat(latestConnState).isEqualTo(DataConnectionState.Disconnected)
assertThat(latestNetType).isNotEqualTo(ResolvedNetworkType.CarrierMergedNetworkType)
@@ -104,7 +104,7 @@
underTest.dataConnectionState.onEach { latestConnState = it }.launchIn(this)
val netJob = underTest.resolvedNetworkType.onEach { latestNetType = it }.launchIn(this)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = NET_ID, level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(level = 1))
assertThat(latestConnState).isEqualTo(DataConnectionState.Disconnected)
assertThat(latestNetType).isNotEqualTo(ResolvedNetworkType.CarrierMergedNetworkType)
@@ -123,8 +123,7 @@
wifiRepository.setIsWifiDefault(true)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId = NET_ID,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -144,8 +143,7 @@
wifiRepository.setIsWifiEnabled(true)
wifiRepository.setIsWifiDefault(true)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId = NET_ID,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -182,8 +180,7 @@
val typeJob = underTest.resolvedNetworkType.onEach { latestType = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId = NET_ID,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID + 10,
level = 3,
)
@@ -204,8 +201,7 @@
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId = NET_ID,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -225,8 +221,7 @@
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId = NET_ID,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -245,8 +240,7 @@
val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId = NET_ID,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 1,
numberOfLevels = 6,
@@ -309,8 +303,7 @@
whenever(telephonyManager.simOperatorName).thenReturn("New SIM name")
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId = NET_ID,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -331,6 +324,5 @@
private companion object {
const val SUB_ID = 123
- const val NET_ID = 456
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index fd4c370..fd23655 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -29,8 +29,8 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.logcatTableLogBuffer
+import com.android.systemui.log.table.tableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
@@ -42,11 +42,11 @@
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -73,23 +73,16 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class FullMobileConnectionRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var underTest: FullMobileConnectionRepository
private val flags =
FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
- private val systemClock = FakeSystemClock()
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
- private val tableLogBuffer =
- TableLogBuffer(
- maxSize = 100,
- name = "TestName",
- systemClock,
- mock(),
- testDispatcher,
- testScope.backgroundScope,
- )
+ private val tableLogBuffer = logcatTableLogBuffer(kosmos, "TestName")
private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
private val connectivityManager = mock<ConnectivityManager>()
@@ -372,19 +365,10 @@
@Test
fun factory_reusesLogBuffersForSameConnection() =
testScope.runTest {
- val realLoggerFactory =
- TableLogBufferFactory(
- mock(),
- FakeSystemClock(),
- mock(),
- testDispatcher,
- testScope.backgroundScope,
- )
-
val factory =
FullMobileConnectionRepository.Factory(
scope = testScope.backgroundScope,
- realLoggerFactory,
+ kosmos.tableLogBufferFactory,
mobileFactory,
carrierMergedFactory,
)
@@ -416,19 +400,10 @@
@Test
fun factory_reusesLogBuffersForSameSubIDevenIfCarrierMerged() =
testScope.runTest {
- val realLoggerFactory =
- TableLogBufferFactory(
- mock(),
- FakeSystemClock(),
- mock(),
- testDispatcher,
- testScope.backgroundScope,
- )
-
val factory =
FullMobileConnectionRepository.Factory(
scope = testScope.backgroundScope,
- realLoggerFactory,
+ kosmos.tableLogBufferFactory,
mobileFactory,
carrierMergedFactory,
)
@@ -512,10 +487,8 @@
val job = underTest.primaryLevel.launchIn(this)
// WHEN we set up carrier merged info
- val networkId = 2
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId,
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 3,
)
@@ -526,8 +499,7 @@
// WHEN we update the info
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId,
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 1,
)
@@ -565,10 +537,8 @@
assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
// WHEN isCarrierMerged is set to true
- val networkId = 2
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId,
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 3,
)
@@ -580,8 +550,7 @@
// WHEN the carrier merge network is updated
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId,
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 4,
)
@@ -632,10 +601,8 @@
.onSignalStrengthsChanged(signalStrength)
// THEN updates to the carrier merged level aren't logged
- val networkId = 2
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId,
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 4,
)
@@ -643,8 +610,7 @@
assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}4")
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
- networkId,
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 3,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 171520f..7634490 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -88,7 +88,6 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo
@@ -121,7 +120,6 @@
@RunWith(AndroidJUnit4::class)
class MobileConnectionRepositoryTest : SysuiTestCase() {
private lateinit var underTest: MobileConnectionRepositoryImpl
- private lateinit var connectionsRepo: FakeMobileConnectionsRepository
private val flags =
FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
@@ -156,8 +154,6 @@
MockitoAnnotations.initMocks(this)
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
- connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
-
underTest =
MobileConnectionRepositoryImpl(
SUB_1_ID,
@@ -821,7 +817,7 @@
captor.lastValue.onReceive(context, intent)
// spnIntent() sets all values to true and test strings
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
job.cancel()
}
@@ -856,7 +852,7 @@
verify(context).registerReceiver(captor.capture(), any())
captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
// WHEN an intent with a different subId is sent
val wrongSubIntent = spnIntent(subId = 101)
@@ -864,7 +860,7 @@
captor.lastValue.onReceive(context, wrongSubIntent)
// THEN the previous intent's name is still used
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
job.cancel()
}
@@ -906,7 +902,7 @@
verify(context).registerReceiver(captor.capture(), any())
captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
val intentWithoutInfo =
spnIntent(
@@ -965,7 +961,7 @@
// The value is still there despite no active subscribers
assertThat(underTest.networkName.value)
- .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
}
@Test
@@ -990,7 +986,7 @@
@Test
@EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
- fun networkName_allFieldsSet_doesNotUseDataSpn() =
+ fun networkName_allFieldsSet_prioritizesDataSpnOverSpn() =
testScope.runTest {
val latest by collectLastValue(underTest.networkName)
val captor = argumentCaptor<BroadcastReceiver>()
@@ -1006,6 +1002,27 @@
plmn = PLMN,
)
captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_spnAndPlmn_fallbackToSpnWhenNullDataSpn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = null,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
}
@@ -1047,7 +1064,27 @@
plmn = PLMN,
)
captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNotNull_showSpn_spnNotNull_dataSpnNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = null,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
}
@Test
@@ -1106,10 +1143,50 @@
plmn = null,
)
captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNull_showSpn_dataSpnNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = null,
+ showPlmn = true,
+ plmn = null,
+ )
+ captor.lastValue.onReceive(context, intent)
assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$SPN"))
}
@Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNull_showSpn_bothSpnNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = null,
+ dataSpn = null,
+ showPlmn = true,
+ plmn = null,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_showPlmn_plmnNull_showSpn_flagOff() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 2ab8c0a..0d82c79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -42,7 +42,6 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -97,7 +96,6 @@
@SmallTest
class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
private lateinit var underTest: MobileConnectionRepositoryImpl
- private lateinit var connectionsRepo: FakeMobileConnectionsRepository
private val flags =
FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
@@ -123,12 +121,6 @@
MockitoAnnotations.initMocks(this)
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
- connectionsRepo =
- FakeMobileConnectionsRepository(
- mobileMappings,
- tableLogger,
- )
-
underTest =
MobileConnectionRepositoryImpl(
SUB_1_ID,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 76982ae..4b6e313 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -28,7 +28,6 @@
import android.net.vcn.VcnTransportInfo
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
-import android.os.Bundle
import android.os.ParcelUuid
import android.telephony.CarrierConfigManager
import android.telephony.ServiceState
@@ -56,7 +55,6 @@
import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
@@ -74,7 +72,6 @@
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
@@ -98,6 +95,7 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@@ -108,11 +106,7 @@
class MobileConnectionsRepositoryTest : SysuiTestCase() {
private val flags =
- FakeFeatureFlagsClassic().also {
- it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true)
- it.set(Flags.INSTANT_TETHER, true)
- it.set(Flags.WIFI_SECONDARY_NETWORKS, true)
- }
+ FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory
private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
@@ -191,7 +185,6 @@
wifiRepository =
WifiRepositoryImpl(
- flags,
testScope.backgroundScope,
mainExecutor,
testDispatcher,
@@ -602,47 +595,85 @@
@SuppressLint("UnspecifiedRegisterReceiverFlag")
@Test
- fun testDeviceServiceStateFromBroadcast_eagerlyWatchesBroadcast() =
+ fun testDeviceEmergencyCallState_eagerlyChecksState() =
testScope.runTest {
- // Value starts out empty (null)
- assertThat(underTest.deviceServiceState.value).isNull()
+ // Value starts out false
+ assertThat(underTest.isDeviceEmergencyCallCapable.value).isFalse()
+ whenever(telephonyManager.activeModemCount).thenReturn(1)
+ whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { _ ->
+ ServiceState().apply { isEmergencyOnly = true }
+ }
// WHEN an appropriate intent gets sent out
- val intent = serviceStateIntent(subId = -1, emergencyOnly = false)
+ val intent = serviceStateIntent(subId = -1)
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
intent,
)
runCurrent()
- // THEN the repo's state is updated
- val expected = ServiceStateModel(isEmergencyOnly = false)
- assertThat(underTest.deviceServiceState.value).isEqualTo(expected)
+ // THEN the repo's state is updated despite no listeners
+ assertThat(underTest.isDeviceEmergencyCallCapable.value).isEqualTo(true)
}
@Test
- fun testDeviceServiceStateFromBroadcast_followsSubIdNegativeOne() =
+ fun testDeviceEmergencyCallState_aggregatesAcrossSlots_oneTrue() =
testScope.runTest {
- // device based state tracks -1
- val intent = serviceStateIntent(subId = -1, emergencyOnly = false)
+ val latest by collectLastValue(underTest.isDeviceEmergencyCallCapable)
+
+ // GIVEN there are multiple slots
+ whenever(telephonyManager.activeModemCount).thenReturn(4)
+ // GIVEN only one of them reports ECM
+ whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { invocation ->
+ when (invocation.getArgument(0) as Int) {
+ 0 -> ServiceState().apply { isEmergencyOnly = false }
+ 1 -> ServiceState().apply { isEmergencyOnly = false }
+ 2 -> ServiceState().apply { isEmergencyOnly = true }
+ 3 -> ServiceState().apply { isEmergencyOnly = false }
+ else -> null
+ }
+ }
+
+ // GIVEN a broadcast goes out for the appropriate subID
+ val intent = serviceStateIntent(subId = -1)
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
intent,
)
runCurrent()
- val deviceBasedState = ServiceStateModel(isEmergencyOnly = false)
- assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState)
+ // THEN the device is in ECM, because one of the service states is
+ assertThat(latest).isTrue()
+ }
- // ... and ignores any other subId
- val intent2 = serviceStateIntent(subId = 1, emergencyOnly = true)
+ @Test
+ fun testDeviceEmergencyCallState_aggregatesAcrossSlots_allFalse() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isDeviceEmergencyCallCapable)
+
+ // GIVEN there are multiple slots
+ whenever(telephonyManager.activeModemCount).thenReturn(4)
+ // GIVEN only one of them reports ECM
+ whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { invocation ->
+ when (invocation.getArgument(0) as Int) {
+ 0 -> ServiceState().apply { isEmergencyOnly = false }
+ 1 -> ServiceState().apply { isEmergencyOnly = false }
+ 2 -> ServiceState().apply { isEmergencyOnly = false }
+ 3 -> ServiceState().apply { isEmergencyOnly = false }
+ else -> null
+ }
+ }
+
+ // GIVEN a broadcast goes out for the appropriate subID
+ val intent = serviceStateIntent(subId = -1)
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
- intent2,
+ intent,
)
runCurrent()
- assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState)
+ // THEN the device is in ECM, because one of the service states is
+ assertThat(latest).isFalse()
}
@Test
@@ -1549,15 +1580,8 @@
*/
private fun serviceStateIntent(
subId: Int,
- emergencyOnly: Boolean = false,
): Intent {
- val serviceState = ServiceState().apply { isEmergencyOnly = emergencyOnly }
-
- val bundle = Bundle()
- serviceState.fillInNotifierBundle(bundle)
-
return Intent(Intent.ACTION_SERVICE_STATE).apply {
- putExtras(bundle)
putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index e439aff..4fd830d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -18,7 +18,6 @@
import android.platform.test.annotations.EnableFlags
import android.telephony.CellSignalStrength
-import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -27,12 +26,12 @@
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.CarrierMergedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
-import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G
@@ -40,12 +39,12 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -61,21 +60,18 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileIconInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var underTest: MobileIconInteractor
private val mobileMappingsProxy = FakeMobileMappingsProxy()
private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
- private val subscriptionModel =
- MutableStateFlow(
- SubscriptionModel(
- subscriptionId = SUB_1_ID,
- carrierName = DEFAULT_NAME,
- profileClass = PROFILE_CLASS_UNSET,
- )
+ private val connectionRepository =
+ FakeMobileConnectionRepository(
+ SUB_1_ID,
+ logcatTableLogBuffer(kosmos, "MobileIconInteractorTest"),
)
- private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID, mock())
-
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index cc0eae7..f6d439a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -28,8 +28,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
@@ -37,10 +36,10 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -59,6 +58,8 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileIconsInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var underTest: MobileIconsInteractor
private lateinit var connectivityRepository: FakeConnectivityRepository
private lateinit var connectionsRepository: FakeMobileConnectionsRepository
@@ -72,15 +73,7 @@
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
- private val tableLogBuffer =
- TableLogBuffer(
- 8,
- "MobileIconsInteractorTest",
- FakeSystemClock(),
- mock(),
- testDispatcher,
- testScope.backgroundScope,
- )
+ private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconsInteractorTest")
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
@@ -897,13 +890,11 @@
testScope.runTest {
val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
- connectionsRepository.deviceServiceState.value =
- ServiceStateModel(isEmergencyOnly = true)
+ connectionsRepository.isDeviceEmergencyCallCapable.value = true
assertThat(latest).isTrue()
- connectionsRepository.deviceServiceState.value =
- ServiceStateModel(isEmergencyOnly = false)
+ connectionsRepository.isDeviceEmergencyCallCapable.value = false
assertThat(latest).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index 42cb660..84846a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -28,12 +28,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,13 +59,13 @@
@RunWithLooper(setAsMainLooper = true)
@OptIn(ExperimentalCoroutinesApi::class)
class ModernStatusBarMobileViewTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private lateinit var testableLooper: TestableLooper
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
- @Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var viewLogger: MobileViewLogger
@Mock private lateinit var constants: ConnectivityConstants
private lateinit var interactor: FakeMobileIconInteractor
@@ -88,10 +89,11 @@
AirplaneModeInteractor(
airplaneModeRepository,
FakeConnectivityRepository(),
- FakeMobileConnectionsRepository(),
+ kosmos.fakeMobileConnectionsRepository,
)
- interactor = FakeMobileIconInteractor(tableLogBuffer)
+ interactor =
+ FakeMobileIconInteractor(logcatTableLogBuffer(kosmos, "ModernStatusBarMobileViewTest"))
createViewModel()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index deb9fcf..f99fcac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -21,24 +21,23 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -59,13 +58,15 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class LocationBasedMobileIconViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var commonImpl: MobileIconViewModelCommon
private lateinit var homeIcon: HomeMobileIconViewModel
private lateinit var qsIcon: QsMobileIconViewModel
private lateinit var keyguardIcon: KeyguardMobileIconViewModel
private lateinit var iconsInteractor: MobileIconsInteractor
private lateinit var interactor: MobileIconInteractor
- private lateinit var connectionsRepository: FakeMobileConnectionsRepository
+ private val connectionsRepository = kosmos.fakeMobileConnectionsRepository
private lateinit var repository: FakeMobileConnectionRepository
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
@@ -76,9 +77,9 @@
it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
}
- @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var constants: ConnectivityConstants
- @Mock private lateinit var tableLogBuffer: TableLogBuffer
+ private val tableLogBuffer =
+ logcatTableLogBuffer(kosmos, "LocationBasedMobileIconViewModelTest")
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
private val testDispatcher = UnconfinedTestDispatcher()
@@ -91,10 +92,8 @@
AirplaneModeInteractor(
FakeAirplaneModeRepository(),
FakeConnectivityRepository(),
- FakeMobileConnectionsRepository(),
+ connectionsRepository,
)
- connectionsRepository =
- FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
repository =
FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
isInService.value = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index e510924..4c7cdfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -34,7 +34,7 @@
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.flags.Flags.NEW_NETWORK_SLICE_UI
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
@@ -51,6 +52,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -74,6 +76,8 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileIconViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private var connectivityRepository = FakeConnectivityRepository()
private lateinit var underTest: MobileIconViewModel
@@ -84,7 +88,7 @@
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
@Mock private lateinit var constants: ConnectivityConstants
- @Mock private lateinit var tableLogBuffer: TableLogBuffer
+ private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconViewModelTest")
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
private val flags =
@@ -118,7 +122,7 @@
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
- FakeMobileConnectionsRepository(),
+ kosmos.fakeMobileConnectionsRepository,
)
iconsInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 47899a6..31ba837 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -24,11 +24,10 @@
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
@@ -36,6 +35,7 @@
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertFalse
@@ -58,12 +58,13 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileIconsViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var underTest: MobileIconsViewModel
private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
- @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var constants: ConnectivityConstants
@Mock private lateinit var logger: MobileViewLogger
@Mock private lateinit var verboseLogger: VerboseMobileViewLogger
@@ -79,7 +80,7 @@
AirplaneModeInteractor(
FakeAirplaneModeRepository(),
FakeConnectivityRepository(),
- FakeMobileConnectionsRepository(),
+ kosmos.fakeMobileConnectionsRepository,
)
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index dbb77d5..c0a206a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -574,7 +574,7 @@
val latest by collectLastValue(underTest.isWifiActive)
// WHEN wifi is active
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(level = 1))
// THEN the interactor returns true due to the wifi network being active
assertThat(latest).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index bf31f1e..e7e4969 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -375,7 +375,7 @@
repo.isSatelliteProvisioned.value = true
// GIVEN wifi network is active
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(level = 1))
// THEN icon is null because the device is connected to wifi
assertThat(latest).isNull()
@@ -573,7 +573,7 @@
repo.isSatelliteProvisioned.value = true
// GIVEN wifi network is active
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(level = 1))
// THEN carrier text is null because the device is connected to wifi
assertThat(latest).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 60750cf..7ae6ea5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -415,9 +415,9 @@
}
@Test
- fun ongoingActivityChip_matchesViewModel() =
+ fun primaryOngoingActivityChip_matchesViewModel() =
testScope.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChip)
+ val latest by collectLastValue(underTest.primaryOngoingActivityChip)
kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
index cefdf7e..4834d36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
+import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -28,9 +29,11 @@
override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>()
- override val ongoingActivityChip: MutableStateFlow<OngoingActivityChipModel> =
+ override val primaryOngoingActivityChip: MutableStateFlow<OngoingActivityChipModel> =
MutableStateFlow(OngoingActivityChipModel.Hidden())
+ override val ongoingActivityChips = MutableStateFlow(MultipleOngoingActivityChipsModel())
+
override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index 2238bff..975e2ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -26,7 +26,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons
@@ -49,6 +49,7 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -61,6 +62,8 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class InternetTileViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var underTest: InternetTileViewModel
private lateinit var mobileIconsInteractor: MobileIconsInteractor
@@ -73,7 +76,7 @@
private val wifiInteractor =
WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
- private val tableLogBuffer: TableLogBuffer = mock()
+ private val tableLogBuffer = logcatTableLogBuffer(kosmos, "InternetTileViewModelTest")
private val carrierConfigTracker: CarrierConfigTracker = mock()
private val mobileConnectionsRepository =
@@ -151,8 +154,7 @@
val latest by collectLastValue(underTest.tileModel)
val networkModel =
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
)
@@ -181,8 +183,7 @@
val latest by collectLastValue(underTest.tileModel)
val networkModel =
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
@@ -294,7 +295,7 @@
testScope.runTest {
val latest by collectLastValue(underTest.tileModel)
- val networkModel = WifiNetworkModel.Inactive
+ val networkModel = WifiNetworkModel.Inactive()
connectivityRepository.setWifiConnected(validated = false)
wifiRepository.setIsWifiDefault(true)
@@ -309,7 +310,7 @@
testScope.runTest {
val latest by collectLastValue(underTest.tileModel)
- val networkModel = WifiNetworkModel.Inactive
+ val networkModel = WifiNetworkModel.Inactive()
connectivityRepository.setWifiConnected(validated = false)
wifiRepository.setIsWifiDefault(true)
@@ -389,8 +390,7 @@
private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
val networkModel =
- WifiNetworkModel.Active(
- networkId = 1,
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
hotspotDeviceType = hotspot,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index f8d50f5..fd4b77d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -25,8 +25,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
@@ -76,7 +74,6 @@
// inside each test case without needing to manually recreate the repository.
private val underTest: WifiRepositoryImpl by lazy {
WifiRepositoryImpl(
- featureFlags,
testScope.backgroundScope,
executor,
dispatcher,
@@ -89,7 +86,6 @@
private val executor = FakeExecutor(FakeSystemClock())
private val logger = LogBuffer("name", maxSize = 100, logcatEchoTracker = mock())
- private val featureFlags = FakeFeatureFlags()
private val tableLogger = mock<TableLogBuffer>()
private val wifiManager =
mock<WifiManager>().apply { whenever(this.maxSignalLevel).thenReturn(10) }
@@ -103,8 +99,6 @@
@Before
fun setUp() {
- featureFlags.set(Flags.INSTANT_TETHER, false)
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, false)
whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor), any()))
.thenReturn(wifiPickerTracker)
}
@@ -289,27 +283,6 @@
}
@Test
- fun accessPointInfo_alwaysFalse() =
- testScope.runTest {
- val latest by collectLastValue(underTest.wifiNetwork)
-
- val wifiEntry =
- mock<WifiEntry>().apply {
- whenever(this.isPrimaryNetwork).thenReturn(true)
- whenever(this.level).thenReturn(3)
- whenever(this.title).thenReturn(TITLE)
- }
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
- getCallback().onWifiEntriesChanged()
-
- assertThat(latest is WifiNetworkModel.Active).isTrue()
- val latestActive = latest as WifiNetworkModel.Active
- assertThat(latestActive.isPasspointAccessPoint).isFalse()
- assertThat(latestActive.isOnlineSignUpForPasspointAccessPoint).isFalse()
- assertThat(latestActive.passpointProviderFriendlyName).isNull()
- }
-
- @Test
fun wifiNetwork_unreachableLevel_inactiveNetwork() =
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
@@ -322,7 +295,10 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Inactive::class.java)
+ val inactiveReason = (latest as WifiNetworkModel.Inactive).inactiveReason
+ assertThat(inactiveReason).contains("level")
+ assertThat(inactiveReason).contains("$WIFI_LEVEL_UNREACHABLE")
}
@Test
@@ -338,7 +314,10 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Inactive::class.java)
+ val inactiveReason = (latest as WifiNetworkModel.Inactive).inactiveReason
+ assertThat(inactiveReason).contains("level")
+ assertThat(inactiveReason).contains("${WIFI_LEVEL_MAX + 1}")
}
@Test
@@ -354,7 +333,10 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Inactive::class.java)
+ val inactiveReason = (latest as WifiNetworkModel.Inactive).inactiveReason
+ assertThat(inactiveReason).contains("level")
+ assertThat(inactiveReason).contains("${WIFI_LEVEL_MIN - 1}")
}
@Test
@@ -394,7 +376,6 @@
@Test
fun wifiNetwork_notHotspot_none() =
testScope.runTest {
- featureFlags.set(Flags.INSTANT_TETHER, true)
val latest by collectLastValue(underTest.wifiNetwork)
val wifiEntry =
@@ -409,7 +390,6 @@
@Test
fun wifiNetwork_hotspot_unknown() =
testScope.runTest {
- featureFlags.set(Flags.INSTANT_TETHER, true)
val latest by collectLastValue(underTest.wifiNetwork)
val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_UNKNOWN)
@@ -423,7 +403,6 @@
@Test
fun wifiNetwork_hotspot_phone() =
testScope.runTest {
- featureFlags.set(Flags.INSTANT_TETHER, true)
val latest by collectLastValue(underTest.wifiNetwork)
val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_PHONE)
@@ -437,7 +416,6 @@
@Test
fun wifiNetwork_hotspot_tablet() =
testScope.runTest {
- featureFlags.set(Flags.INSTANT_TETHER, true)
val latest by collectLastValue(underTest.wifiNetwork)
val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_TABLET)
@@ -451,7 +429,6 @@
@Test
fun wifiNetwork_hotspot_laptop() =
testScope.runTest {
- featureFlags.set(Flags.INSTANT_TETHER, true)
val latest by collectLastValue(underTest.wifiNetwork)
val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_LAPTOP)
@@ -465,7 +442,6 @@
@Test
fun wifiNetwork_hotspot_watch() =
testScope.runTest {
- featureFlags.set(Flags.INSTANT_TETHER, true)
val latest by collectLastValue(underTest.wifiNetwork)
val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_WATCH)
@@ -479,7 +455,6 @@
@Test
fun wifiNetwork_hotspot_auto() =
testScope.runTest {
- featureFlags.set(Flags.INSTANT_TETHER, true)
val latest by collectLastValue(underTest.wifiNetwork)
val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_AUTO)
@@ -493,7 +468,6 @@
@Test
fun wifiNetwork_hotspot_invalid() =
testScope.runTest {
- featureFlags.set(Flags.INSTANT_TETHER, true)
val latest by collectLastValue(underTest.wifiNetwork)
val wifiEntry = createHotspotWithType(1234)
@@ -505,23 +479,6 @@
}
@Test
- fun wifiNetwork_hotspot_flagOff_valueNotUsed() =
- testScope.runTest {
- // WHEN the flag is off
- featureFlags.set(Flags.INSTANT_TETHER, false)
-
- val latest by collectLastValue(underTest.wifiNetwork)
-
- val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_WATCH)
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
- getCallback().onWifiEntriesChanged()
-
- // THEN NONE is always used, even if the wifi entry does have a hotspot device type
- assertThat((latest as WifiNetworkModel.Active).hotspotDeviceType)
- .isEqualTo(WifiNetworkModel.HotspotDeviceType.NONE)
- }
-
- @Test
fun wifiNetwork_isCarrierMerged_flowHasCarrierMerged() =
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
@@ -582,6 +539,25 @@
}
@Test
+ fun wifiNetwork_carrierMergedButInvalidLevel_flowHasInvalid() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiNetwork)
+
+ val mergedEntry =
+ mock<MergedCarrierEntry>().apply {
+ whenever(this.isPrimaryNetwork).thenReturn(true)
+ whenever(this.subscriptionId).thenReturn(3)
+ whenever(this.isDefaultNetwork).thenReturn(true)
+ whenever(this.level).thenReturn(WIFI_LEVEL_UNREACHABLE)
+ }
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
+
+ getCallback().onWifiEntriesChanged()
+
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Invalid::class.java)
+ }
+
+ @Test
fun wifiNetwork_notValidated_networkNotValidated() =
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
@@ -623,7 +599,7 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isEqualTo(WifiNetworkModel.Inactive())
}
@Test
@@ -639,7 +615,7 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isEqualTo(WifiNetworkModel.Inactive())
}
@Test
@@ -826,7 +802,6 @@
@Test
fun secondaryNetworks_activeEntriesEmpty_isEmpty() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
whenever(wifiPickerTracker.activeWifiEntries).thenReturn(listOf())
@@ -839,7 +814,6 @@
@Test
fun secondaryNetworks_oneActiveEntry_hasOne() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val wifiEntry = mock<WifiEntry>()
@@ -853,7 +827,6 @@
@Test
fun secondaryNetworks_multipleActiveEntries_hasMultiple() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val wifiEntry1 = mock<WifiEntry>()
@@ -868,7 +841,6 @@
@Test
fun secondaryNetworks_mapsToInactive() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val inactiveEntry =
@@ -884,7 +856,6 @@
@Test
fun secondaryNetworks_mapsToActive() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val activeEntry = mock<WifiEntry>().apply { whenever(this.level).thenReturn(2) }
@@ -900,7 +871,6 @@
@Test
fun secondaryNetworks_mapsToCarrierMerged() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val carrierMergedEntry =
@@ -917,7 +887,6 @@
@Test
fun secondaryNetworks_mapsMultipleInOrder() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val activeEntry = mock<WifiEntry>().apply { whenever(this.level).thenReturn(2) }
@@ -937,7 +906,6 @@
@Test
fun secondaryNetworks_filtersOutConnectedEntry() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val connectedEntry = mock<WifiEntry>().apply { whenever(this.level).thenReturn(1) }
@@ -959,7 +927,6 @@
@Test
fun secondaryNetworks_noConnectedEntry_hasAllActiveEntries() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val secondaryEntry1 = mock<WifiEntry>().apply { whenever(this.level).thenReturn(2) }
@@ -978,7 +945,6 @@
@Test
fun secondaryNetworks_filtersOutPrimaryNetwork() =
testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
val latest by collectLastValue(underTest.secondaryNetworks)
val primaryEntry =
@@ -1001,20 +967,6 @@
}
@Test
- fun secondaryNetworks_flagOff_noNetworks() =
- testScope.runTest {
- featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, false)
- val latest by collectLastValue(underTest.secondaryNetworks)
-
- val wifiEntry = mock<WifiEntry>()
- whenever(wifiPickerTracker.activeWifiEntries).thenReturn(listOf(wifiEntry))
-
- getCallback().onWifiEntriesChanged()
-
- assertThat(latest).isEmpty()
- }
-
- @Test
fun isWifiConnectedWithValidSsid_inactiveNetwork_false() =
testScope.runTest {
collectLastValue(underTest.wifiNetwork)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
index eb6b068..1495519 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
+import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,62 +33,109 @@
@RunWith(AndroidJUnit4::class)
class WifiNetworkModelTest : SysuiTestCase() {
@Test
- fun active_levelsInValidRange_noException() {
+ fun active_levelsInValidRange_createsActive() {
(MIN_VALID_LEVEL..MAX_VALID_LEVEL).forEach { level ->
- WifiNetworkModel.Active(NETWORK_ID, level = level)
- // No assert, just need no crash
+ val result = WifiNetworkModel.Active.of(level = level)
+ assertThat(result).isInstanceOf(WifiNetworkModel.Active::class.java)
}
}
- @Test(expected = IllegalArgumentException::class)
- fun active_levelNegative_exceptionThrown() {
- WifiNetworkModel.Active(NETWORK_ID, level = MIN_VALID_LEVEL - 1)
+ fun active_levelTooLow_returnsInactive() {
+ val result = WifiNetworkModel.Active.of(level = MIN_VALID_LEVEL - 1)
+ assertThat(result).isInstanceOf(WifiNetworkModel.Inactive::class.java)
}
@Test(expected = IllegalArgumentException::class)
- fun active_levelTooHigh_exceptionThrown() {
- WifiNetworkModel.Active(NETWORK_ID, level = MAX_VALID_LEVEL + 1)
+ fun active_levelTooLow_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.Active.of(level = MIN_VALID_LEVEL)
+
+ (starting as WifiNetworkModel.Active).copy(level = MIN_VALID_LEVEL - 1)
+ }
+
+ fun active_levelTooHigh_returnsInactive() {
+ val result = WifiNetworkModel.Active.of(level = MAX_VALID_LEVEL + 1)
+
+ assertThat(result).isInstanceOf(WifiNetworkModel.Inactive::class.java)
}
@Test(expected = IllegalArgumentException::class)
- fun carrierMerged_invalidSubId_exceptionThrown() {
- WifiNetworkModel.CarrierMerged(NETWORK_ID, INVALID_SUBSCRIPTION_ID, 1)
+ fun active_levelTooHigh_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.Active.of(level = MAX_VALID_LEVEL)
+
+ (starting as WifiNetworkModel.Active).copy(level = MAX_VALID_LEVEL + 1)
+ }
+
+ fun active_levelUnreachable_returnsInactive() {
+ val result = WifiNetworkModel.Active.of(level = WIFI_LEVEL_UNREACHABLE)
+
+ assertThat(result).isInstanceOf(WifiNetworkModel.Inactive::class.java)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun active_levelUnreachable_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.Active.of(level = MAX_VALID_LEVEL)
+
+ (starting as WifiNetworkModel.Active).copy(level = WIFI_LEVEL_UNREACHABLE)
+ }
+
+ fun carrierMerged_invalidSubId_returnsInvalid() {
+ val result = WifiNetworkModel.CarrierMerged.of(INVALID_SUBSCRIPTION_ID, level = 1)
+
+ assertThat(result).isInstanceOf(WifiNetworkModel.Invalid::class.java)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun carrierMerged_invalidSubId_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1)
+
+ (starting as WifiNetworkModel.CarrierMerged).copy(subscriptionId = INVALID_SUBSCRIPTION_ID)
+ }
+
+ fun carrierMerged_levelUnreachable_returnsInvalid() {
+ val result =
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = WIFI_LEVEL_UNREACHABLE)
+
+ assertThat(result).isInstanceOf(WifiNetworkModel.Invalid::class.java)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun carrierMerged_levelUnreachable_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1)
+
+ (starting as WifiNetworkModel.CarrierMerged).copy(level = WIFI_LEVEL_UNREACHABLE)
}
@Test
fun active_hasValidSsid_nullSsid_false() {
val network =
- WifiNetworkModel.Active(
- NETWORK_ID,
+ WifiNetworkModel.Active.of(
level = MAX_VALID_LEVEL,
ssid = null,
)
- assertThat(network.hasValidSsid()).isFalse()
+ assertThat((network as WifiNetworkModel.Active).hasValidSsid()).isFalse()
}
@Test
fun active_hasValidSsid_unknownSsid_false() {
val network =
- WifiNetworkModel.Active(
- NETWORK_ID,
+ WifiNetworkModel.Active.of(
level = MAX_VALID_LEVEL,
ssid = UNKNOWN_SSID,
)
- assertThat(network.hasValidSsid()).isFalse()
+ assertThat((network as WifiNetworkModel.Active).hasValidSsid()).isFalse()
}
@Test
fun active_hasValidSsid_validSsid_true() {
val network =
- WifiNetworkModel.Active(
- NETWORK_ID,
+ WifiNetworkModel.Active.of(
level = MAX_VALID_LEVEL,
ssid = "FakeSsid",
)
- assertThat(network.hasValidSsid()).isTrue()
+ assertThat((network as WifiNetworkModel.Active).hasValidSsid()).isTrue()
}
// Non-exhaustive logDiffs test -- just want to make sure the logging logic isn't totally broken
@@ -96,16 +144,15 @@
fun logDiffs_carrierMergedToInactive_resetsAllFields() {
val logger = TestLogger()
val prevVal =
- WifiNetworkModel.CarrierMerged(
- networkId = 5,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = 3,
level = 1,
)
- WifiNetworkModel.Inactive.logDiffs(prevVal, logger)
+ WifiNetworkModel.Inactive(inactiveReason = "TestReason").logDiffs(prevVal, logger)
- assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
- assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
+ assertThat(logger.changes)
+ .contains(Pair(COL_NETWORK_TYPE, "$TYPE_INACTIVE[reason=TestReason]"))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
@@ -115,16 +162,14 @@
fun logDiffs_inactiveToCarrierMerged_logsAllFields() {
val logger = TestLogger()
val carrierMerged =
- WifiNetworkModel.CarrierMerged(
- networkId = 6,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = 3,
level = 2,
)
- carrierMerged.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
+ carrierMerged.logDiffs(prevVal = WifiNetworkModel.Inactive(), logger)
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
- assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "6"))
assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
assertThat(logger.changes).contains(Pair(COL_LEVEL, "2"))
@@ -135,38 +180,33 @@
fun logDiffs_inactiveToActive_logsAllActiveFields() {
val logger = TestLogger()
val activeNetwork =
- WifiNetworkModel.Active(
- networkId = 5,
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 3,
ssid = "Test SSID",
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.LAPTOP,
)
- activeNetwork.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
+ activeNetwork.logDiffs(prevVal = WifiNetworkModel.Inactive(), logger)
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
- assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "5"))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
assertThat(logger.changes).contains(Pair(COL_LEVEL, "3"))
assertThat(logger.changes).contains(Pair(COL_SSID, "Test SSID"))
assertThat(logger.changes).contains(Pair(COL_HOTSPOT, "LAPTOP"))
}
+
@Test
fun logDiffs_activeToInactive_resetsAllActiveFields() {
val logger = TestLogger()
val activeNetwork =
- WifiNetworkModel.Active(
- networkId = 5,
- isValidated = true,
- level = 3,
- ssid = "Test SSID"
- )
+ WifiNetworkModel.Active.of(isValidated = true, level = 3, ssid = "Test SSID")
- WifiNetworkModel.Inactive.logDiffs(prevVal = activeNetwork, logger)
+ WifiNetworkModel.Inactive(inactiveReason = "TestReason")
+ .logDiffs(prevVal = activeNetwork, logger)
- assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
- assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
+ assertThat(logger.changes)
+ .contains(Pair(COL_NETWORK_TYPE, "$TYPE_INACTIVE[reason=TestReason]"))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
@@ -177,16 +217,14 @@
fun logDiffs_carrierMergedToActive_logsAllActiveFields() {
val logger = TestLogger()
val activeNetwork =
- WifiNetworkModel.Active(
- networkId = 5,
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 3,
ssid = "Test SSID",
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.AUTO,
)
val prevVal =
- WifiNetworkModel.CarrierMerged(
- networkId = 5,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = 3,
level = 1,
)
@@ -194,25 +232,19 @@
activeNetwork.logDiffs(prevVal, logger)
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
- assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "5"))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
assertThat(logger.changes).contains(Pair(COL_LEVEL, "3"))
assertThat(logger.changes).contains(Pair(COL_SSID, "Test SSID"))
assertThat(logger.changes).contains(Pair(COL_HOTSPOT, "AUTO"))
}
+
@Test
fun logDiffs_activeToCarrierMerged_logsAllFields() {
val logger = TestLogger()
val activeNetwork =
- WifiNetworkModel.Active(
- networkId = 5,
- isValidated = true,
- level = 3,
- ssid = "Test SSID"
- )
+ WifiNetworkModel.Active.of(isValidated = true, level = 3, ssid = "Test SSID")
val carrierMerged =
- WifiNetworkModel.CarrierMerged(
- networkId = 6,
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = 3,
level = 2,
)
@@ -220,7 +252,6 @@
carrierMerged.logDiffs(prevVal = activeNetwork, logger)
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
- assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "6"))
assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
assertThat(logger.changes).contains(Pair(COL_LEVEL, "2"))
@@ -231,19 +262,9 @@
fun logDiffs_activeChangesLevel_onlyLevelLogged() {
val logger = TestLogger()
val prevActiveNetwork =
- WifiNetworkModel.Active(
- networkId = 5,
- isValidated = true,
- level = 3,
- ssid = "Test SSID"
- )
+ WifiNetworkModel.Active.of(isValidated = true, level = 3, ssid = "Test SSID")
val newActiveNetwork =
- WifiNetworkModel.Active(
- networkId = 5,
- isValidated = true,
- level = 2,
- ssid = "Test SSID"
- )
+ WifiNetworkModel.Active.of(isValidated = true, level = 2, ssid = "Test SSID")
newActiveNetwork.logDiffs(prevActiveNetwork, logger)
@@ -265,8 +286,4 @@
changes.add(Pair(columnName, value.toString()))
}
}
-
- companion object {
- private const val NETWORK_ID = 2
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 80b10c0..37c7a48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -26,7 +26,7 @@
import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
@@ -36,7 +36,7 @@
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -61,10 +62,11 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
class ModernStatusBarWifiViewTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private lateinit var testableLooper: TestableLooper
- @Mock private lateinit var tableLogBuffer: TableLogBuffer
+ private val tableLogBuffer = logcatTableLogBuffer(kosmos, "ModernStatusBarWifiViewTest")
@Mock private lateinit var connectivityConstants: ConnectivityConstants
@Mock private lateinit var wifiConstants: WifiConstants
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -91,7 +93,7 @@
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
- FakeMobileConnectionsRepository(),
+ kosmos.fakeMobileConnectionsRepository,
),
tableLogBuffer,
scope,
@@ -193,9 +195,7 @@
@Test
fun isIconVisible_notEnabled_outputsFalse() {
wifiRepository.setIsWifiEnabled(false)
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 2)
- )
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(isValidated = true, level = 2))
val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
@@ -210,9 +210,7 @@
@Test
fun isIconVisible_enabled_outputsTrue() {
wifiRepository.setIsWifiEnabled(true)
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 2)
- )
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(isValidated = true, level = 2))
val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
@@ -270,4 +268,3 @@
}
private const val SLOT_NAME = "TestSlotName"
-private const val NETWORK_ID = 200
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index 489319e..96a0194 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -31,7 +31,7 @@
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon.Companion.NO_INTERNET
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -64,6 +65,7 @@
@RunWith(Parameterized::class)
internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase) :
SysuiTestCase() {
+ private val kosmos = testKosmos()
private lateinit var underTest: WifiViewModel
@@ -91,7 +93,7 @@
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
- FakeMobileConnectionsRepository(),
+ kosmos.fakeMobileConnectionsRepository,
),
tableLogBuffer,
scope,
@@ -204,53 +206,51 @@
// Enabled = false => no networks shown
TestCase(
enabled = false,
- network =
- WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
+ network = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1),
expected = null,
),
TestCase(
enabled = false,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected = null,
),
TestCase(
enabled = false,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 1),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 1),
expected = null,
),
TestCase(
enabled = false,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 3),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 3),
expected = null,
),
// forceHidden = true => no networks shown
TestCase(
forceHidden = true,
- network =
- WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
+ network = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1),
expected = null,
),
TestCase(
forceHidden = true,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected = null,
),
TestCase(
enabled = false,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 2),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 2),
expected = null,
),
TestCase(
forceHidden = true,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 1),
expected = null,
),
// alwaysShowIconWhenEnabled = true => all Inactive and Active networks shown
TestCase(
alwaysShowIconWhenEnabled = true,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected =
Expected(
iconResource = WIFI_NO_NETWORK,
@@ -263,7 +263,7 @@
),
TestCase(
alwaysShowIconWhenEnabled = true,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 4),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 4),
expected =
Expected(
iconResource = WIFI_NO_INTERNET_ICONS[4],
@@ -276,7 +276,7 @@
),
TestCase(
alwaysShowIconWhenEnabled = true,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 2),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 2),
expected =
Expected(
iconResource = WIFI_FULL_ICONS[2],
@@ -290,7 +290,7 @@
// hasDataCapabilities = false => all Inactive and Active networks shown
TestCase(
hasDataCapabilities = false,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected =
Expected(
iconResource = WIFI_NO_NETWORK,
@@ -303,7 +303,7 @@
),
TestCase(
hasDataCapabilities = false,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 2),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 2),
expected =
Expected(
iconResource = WIFI_NO_INTERNET_ICONS[2],
@@ -316,7 +316,7 @@
),
TestCase(
hasDataCapabilities = false,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 0),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 0),
expected =
Expected(
iconResource = WIFI_FULL_ICONS[0],
@@ -330,7 +330,7 @@
// isDefault = true => all Inactive and Active networks shown
TestCase(
isDefault = true,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected =
Expected(
iconResource = WIFI_NO_NETWORK,
@@ -343,7 +343,7 @@
),
TestCase(
isDefault = true,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 3),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 3),
expected =
Expected(
iconResource = WIFI_NO_INTERNET_ICONS[3],
@@ -356,7 +356,7 @@
),
TestCase(
isDefault = true,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 1),
expected =
Expected(
iconResource = WIFI_FULL_ICONS[1],
@@ -372,15 +372,14 @@
enabled = true,
isDefault = true,
forceHidden = false,
- network =
- WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
+ network = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1),
expected = null,
),
// isDefault = false => no networks shown
TestCase(
isDefault = false,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected = null,
),
TestCase(
@@ -390,7 +389,7 @@
),
TestCase(
isDefault = false,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 3),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 3),
expected = null,
),
@@ -398,7 +397,7 @@
// because wifi isn't the default connection (b/272509965).
TestCase(
isDefault = false,
- network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 4),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 4),
expected = null,
),
)
@@ -406,4 +405,3 @@
}
private val IMMEDIATE = Dispatchers.Main.immediate
-private const val NETWORK_ID = 789
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
new file mode 100644
index 0000000..cafebb9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import androidx.compose.ui.input.pointer.util.VelocityTracker1D
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NOT_STARTED
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RecentAppsGestureMonitorTest : SysuiTestCase() {
+
+ companion object {
+ const val THRESHOLD_VELOCITY_PX_PER_MS = 0.1f
+ // multiply by 1000 to get px per ms instead of px per s which is unit used by velocity
+ // tracker
+ const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS * 1000 - 1
+ const val FAST = THRESHOLD_VELOCITY_PX_PER_MS * 1000 + 1
+ }
+
+ private var gestureState = NOT_STARTED
+ private val velocityTracker =
+ mock<VelocityTracker1D> {
+ // by default return correct speed for the gesture - as if pointer is slowing down
+ on { calculateVelocity() } doReturn SLOW
+ }
+ private val gestureMonitor =
+ RecentAppsGestureMonitor(
+ gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
+ gestureStateChangedCallback = { gestureState = it },
+ velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS,
+ velocityTracker = velocityTracker
+ )
+
+ @Test
+ fun triggersGestureFinishedForThreeFingerGestureUp() {
+ assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = FINISHED)
+ }
+
+ @Test
+ fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() {
+ whenever(velocityTracker.calculateVelocity()).thenReturn(FAST)
+ assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NOT_STARTED)
+ }
+
+ @Test
+ fun triggersGestureProgressForThreeFingerGestureStarted() {
+ assertStateAfterEvents(
+ events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
+ expectedState = IN_PROGRESS
+ )
+ }
+
+ @Test
+ fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
+ assertStateAfterEvents(
+ events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
+ expectedState = NOT_STARTED
+ )
+ }
+
+ @Test
+ fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
+ assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NOT_STARTED)
+ assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = NOT_STARTED)
+ assertStateAfterEvents(
+ events = ThreeFingerGesture.swipeRight(),
+ expectedState = NOT_STARTED
+ )
+ }
+
+ @Test
+ fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
+ assertStateAfterEvents(events = TwoFingerGesture.swipeUp(), expectedState = NOT_STARTED)
+ }
+
+ @Test
+ fun doesntTriggerGestureFinished_onFourFingersSwipe() {
+ assertStateAfterEvents(events = FourFingerGesture.swipeUp(), expectedState = NOT_STARTED)
+ }
+
+ private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
+ events.forEach { gestureMonitor.processTouchpadEvent(it) }
+ assertThat(gestureState).isEqualTo(expectedState)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 37a73cf..c235954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -24,21 +24,21 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.testKosmos
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
-import com.android.systemui.util.settings.FakeGlobalSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,143 +52,153 @@
@RunWith(AndroidJUnit4::class)
class UserRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.unconfinedTestDispatcher
+ private val testScope = kosmos.unconfinedTestScope
+ private val globalSettings = kosmos.unconfinedDispatcherFakeGlobalSettings
+
@Mock private lateinit var manager: UserManager
private lateinit var underTest: UserRepositoryImpl
- private lateinit var globalSettings: FakeGlobalSettings
private lateinit var tracker: FakeUserTracker
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
- globalSettings = FakeGlobalSettings()
tracker = FakeUserTracker()
}
@Test
- fun userSwitcherSettings() = runSelfCancelingTest {
- setUpGlobalSettings(
- isSimpleUserSwitcher = true,
- isAddUsersFromLockscreen = true,
- isUserSwitcherEnabled = true,
- )
- underTest = create(this)
+ fun userSwitcherSettings() =
+ testScope.runTest {
+ setUpGlobalSettings(
+ isSimpleUserSwitcher = true,
+ isAddUsersFromLockscreen = true,
+ isUserSwitcherEnabled = true,
+ )
+ underTest = create(testScope.backgroundScope)
+ var value: UserSwitcherSettingsModel? = null
+ val job = underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
- var value: UserSwitcherSettingsModel? = null
- underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+ assertUserSwitcherSettings(
+ model = value,
+ expectedSimpleUserSwitcher = true,
+ expectedAddUsersFromLockscreen = true,
+ expectedUserSwitcherEnabled = true,
+ )
- assertUserSwitcherSettings(
- model = value,
- expectedSimpleUserSwitcher = true,
- expectedAddUsersFromLockscreen = true,
- expectedUserSwitcherEnabled = true,
- )
-
- setUpGlobalSettings(
- isSimpleUserSwitcher = false,
- isAddUsersFromLockscreen = true,
- isUserSwitcherEnabled = true,
- )
- assertUserSwitcherSettings(
- model = value,
- expectedSimpleUserSwitcher = false,
- expectedAddUsersFromLockscreen = true,
- expectedUserSwitcherEnabled = true,
- )
- }
+ setUpGlobalSettings(
+ isSimpleUserSwitcher = false,
+ isAddUsersFromLockscreen = true,
+ isUserSwitcherEnabled = true,
+ )
+ assertUserSwitcherSettings(
+ model = value,
+ expectedSimpleUserSwitcher = false,
+ expectedAddUsersFromLockscreen = true,
+ expectedUserSwitcherEnabled = true,
+ )
+ job.cancel()
+ }
@Test
- fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() = runSelfCancelingTest {
- underTest = create(this)
+ fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
- var value: UserSwitcherSettingsModel? = null
- underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+ var value: UserSwitcherSettingsModel? = null
+ val job = underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
- assertUserSwitcherSettings(
- model = value,
- expectedSimpleUserSwitcher = false,
- expectedAddUsersFromLockscreen = false,
- expectedUserSwitcherEnabled =
- context.resources.getBoolean(
- com.android.internal.R.bool.config_showUserSwitcherByDefault
- ),
- )
- }
+ assertUserSwitcherSettings(
+ model = value,
+ expectedSimpleUserSwitcher = false,
+ expectedAddUsersFromLockscreen = false,
+ expectedUserSwitcherEnabled =
+ context.resources.getBoolean(
+ com.android.internal.R.bool.config_showUserSwitcherByDefault
+ ),
+ )
+ job.cancel()
+ }
@Test
- fun refreshUsers() = runSelfCancelingTest {
- val mainUserId = 10
- val mainUser = mock(UserHandle::class.java)
- whenever(manager.mainUser).thenReturn(mainUser)
- whenever(mainUser.identifier).thenReturn(mainUserId)
+ fun refreshUsers() =
+ testScope.runTest {
+ val mainUserId = 10
+ val mainUser = mock(UserHandle::class.java)
+ whenever(manager.mainUser).thenReturn(mainUser)
+ whenever(mainUser.identifier).thenReturn(mainUserId)
- underTest = create(this)
- val initialExpectedValue =
- setUpUsers(
- count = 3,
- selectedIndex = 0,
- )
- var userInfos: List<UserInfo>? = null
- var selectedUserInfo: UserInfo? = null
- underTest.userInfos.onEach { userInfos = it }.launchIn(this)
- underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
+ underTest = create(testScope.backgroundScope)
+ val initialExpectedValue =
+ setUpUsers(
+ count = 3,
+ selectedIndex = 0,
+ )
+ var userInfos: List<UserInfo>? = null
+ var selectedUserInfo: UserInfo? = null
+ val job1 = underTest.userInfos.onEach { userInfos = it }.launchIn(this)
+ val job2 = underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
- underTest.refreshUsers()
- assertThat(userInfos).isEqualTo(initialExpectedValue)
- assertThat(selectedUserInfo).isEqualTo(initialExpectedValue[0])
- assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(initialExpectedValue)
+ assertThat(selectedUserInfo).isEqualTo(initialExpectedValue[0])
+ assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
- val secondExpectedValue =
- setUpUsers(
- count = 4,
- selectedIndex = 1,
- )
- underTest.refreshUsers()
- assertThat(userInfos).isEqualTo(secondExpectedValue)
- assertThat(selectedUserInfo).isEqualTo(secondExpectedValue[1])
- assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
+ val secondExpectedValue =
+ setUpUsers(
+ count = 4,
+ selectedIndex = 1,
+ )
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(secondExpectedValue)
+ assertThat(selectedUserInfo).isEqualTo(secondExpectedValue[1])
+ assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
- val selectedNonGuestUserId = selectedUserInfo?.id
- val thirdExpectedValue =
- setUpUsers(
- count = 2,
- isLastGuestUser = true,
- selectedIndex = 1,
- )
- underTest.refreshUsers()
- assertThat(userInfos).isEqualTo(thirdExpectedValue)
- assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
- assertThat(selectedUserInfo?.isGuest).isTrue()
- assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
- assertThat(underTest.mainUserId).isEqualTo(mainUserId)
- }
+ val selectedNonGuestUserId = selectedUserInfo?.id
+ val thirdExpectedValue =
+ setUpUsers(
+ count = 2,
+ isLastGuestUser = true,
+ selectedIndex = 1,
+ )
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(thirdExpectedValue)
+ assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
+ assertThat(selectedUserInfo?.isGuest).isTrue()
+ assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
+ assertThat(underTest.mainUserId).isEqualTo(mainUserId)
+ job1.cancel()
+ job2.cancel()
+ }
@Test
- fun refreshUsers_sortsByCreationTime_guestUserLast() = runSelfCancelingTest {
- underTest = create(this)
- val unsortedUsers =
- setUpUsers(
- count = 3,
- selectedIndex = 0,
- isLastGuestUser = true,
- )
- unsortedUsers[0].creationTime = 999
- unsortedUsers[1].creationTime = 900
- unsortedUsers[2].creationTime = 950
- val expectedUsers =
- listOf(
- unsortedUsers[1],
- unsortedUsers[0],
- unsortedUsers[2], // last because this is the guest
- )
- var userInfos: List<UserInfo>? = null
- underTest.userInfos.onEach { userInfos = it }.launchIn(this)
+ fun refreshUsers_sortsByCreationTime_guestUserLast() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ val unsortedUsers =
+ setUpUsers(
+ count = 3,
+ selectedIndex = 0,
+ isLastGuestUser = true,
+ )
+ unsortedUsers[0].creationTime = 999
+ unsortedUsers[1].creationTime = 900
+ unsortedUsers[2].creationTime = 950
+ val expectedUsers =
+ listOf(
+ unsortedUsers[1],
+ unsortedUsers[0],
+ unsortedUsers[2], // last because this is the guest
+ )
+ var userInfos: List<UserInfo>? = null
+ val job = underTest.userInfos.onEach { userInfos = it }.launchIn(this)
- underTest.refreshUsers()
- assertThat(userInfos).isEqualTo(expectedUsers)
- }
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(expectedUsers)
+ job.cancel()
+ }
private fun setUpUsers(
count: Int,
@@ -206,58 +216,68 @@
tracker.set(userInfos, selectedIndex)
return userInfos
}
- @Test
- fun userTrackerCallback_updatesSelectedUserInfo() = runSelfCancelingTest {
- underTest = create(this)
- var selectedUserInfo: UserInfo? = null
- underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
- setUpUsers(
- count = 2,
- selectedIndex = 0,
- )
- tracker.onProfileChanged()
- assertThat(selectedUserInfo?.id).isEqualTo(0)
- setUpUsers(
- count = 2,
- selectedIndex = 1,
- )
- tracker.onProfileChanged()
- assertThat(selectedUserInfo?.id).isEqualTo(1)
- }
@Test
- fun userTrackerCallback_updatesSelectionStatus() = runSelfCancelingTest {
- underTest = create(this)
- var selectedUser: SelectedUserModel? = null
- underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
- setUpUsers(count = 2, selectedIndex = 1)
+ fun userTrackerCallback_updatesSelectedUserInfo() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ var selectedUserInfo: UserInfo? = null
+ val job = underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
- // WHEN the user is changing
- tracker.onUserChanging(userId = 1)
+ setUpUsers(
+ count = 2,
+ selectedIndex = 0,
+ )
+ tracker.onProfileChanged()
+ assertThat(selectedUserInfo?.id).isEqualTo(0)
+ setUpUsers(
+ count = 2,
+ selectedIndex = 1,
+ )
+ tracker.onProfileChanged()
+ assertThat(selectedUserInfo?.id).isEqualTo(1)
+ job.cancel()
+ }
- // THEN the selection status is IN_PROGRESS
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ @Test
+ fun userTrackerCallback_updatesSelectionStatus() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ var selectedUser: SelectedUserModel? = null
+ val job = underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
- // WHEN the user has finished changing
- tracker.onUserChanged(userId = 1)
+ setUpUsers(count = 2, selectedIndex = 1)
- // THEN the selection status is COMPLETE
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+ // WHEN the user is changing
+ tracker.onUserChanging(userId = 1)
- tracker.onProfileChanged()
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+ // THEN the selection status is IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus)
+ .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
- setUpUsers(count = 2, selectedIndex = 0)
+ // WHEN the user has finished changing
+ tracker.onUserChanged(userId = 1)
- tracker.onUserChanging(userId = 0)
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ // THEN the selection status is COMPLETE
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
- // WHEN a profile change occurs while a user is changing
- tracker.onProfileChanged()
+ tracker.onProfileChanged()
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
- // THEN the selection status remains as IN_PROGRESS
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
- }
+ setUpUsers(count = 2, selectedIndex = 0)
+
+ tracker.onUserChanging(userId = 0)
+ assertThat(selectedUser!!.selectionStatus)
+ .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // WHEN a profile change occurs while a user is changing
+ tracker.onProfileChanged()
+
+ // THEN the selection status remains as IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus)
+ .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ job.cancel()
+ }
private fun createUserInfo(
id: Int,
@@ -308,26 +328,13 @@
assertThat(model.isUserSwitcherEnabled).isEqualTo(expectedUserSwitcherEnabled)
}
- /**
- * Executes the given block of execution within the scope of a dedicated [CoroutineScope] which
- * is then automatically canceled and cleaned-up.
- */
- private fun runSelfCancelingTest(
- block: suspend CoroutineScope.() -> Unit,
- ) =
- runBlocking(Dispatchers.Main.immediate) {
- val scope = CoroutineScope(coroutineContext + Job())
- block(scope)
- scope.cancel()
- }
-
- private fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl {
+ private fun create(scope: CoroutineScope): UserRepositoryImpl {
return UserRepositoryImpl(
appContext = context,
manager = manager,
applicationScope = scope,
- mainDispatcher = IMMEDIATE,
- backgroundDispatcher = IMMEDIATE,
+ mainDispatcher = testDispatcher,
+ backgroundDispatcher = testDispatcher,
globalSettings = globalSettings,
tracker = tracker,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
index 88b2630..a0cfab4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
@@ -23,12 +23,13 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -40,9 +41,10 @@
@RunWith(AndroidJUnit4::class)
class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() {
- private val dispatcher = StandardTestDispatcher()
- private val testScope = TestScope(dispatcher)
- private val secureSettings = FakeSettings()
+ private val kosmos = testKosmos()
+ private val dispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val secureSettings = kosmos.fakeSettings
private val userRepository = Kosmos().fakeUserRepository
private lateinit var repository: UserAwareSecureSettingsRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 6e39365..3e7980d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -184,11 +184,11 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt
index 60d97d1..8541d77 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.bouncer.shared.flag
+package com.android.settingslib.notification.modes
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.google.common.util.concurrent.MoreExecutors
-var Kosmos.fakeComposeBouncerFlags by Kosmos.Fixture { FakeComposeBouncerFlags() }
-val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
+val Kosmos.zenIconLoader by Fixture { ZenIconLoader(MoreExecutors.newDirectExecutorService()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
index 0358474..3041240 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.UserHandle;
@@ -40,6 +41,7 @@
@GuardedBy("mRegisteredReceivers")
private final Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
private final Map<UserHandle, Context> mContextForUser = new HashMap<>();
+ private final Map<String, Context> mContextForPackage = new HashMap<>();
public SysuiTestableContext(Context base) {
super(base);
@@ -175,4 +177,22 @@
}
return super.createContextAsUser(user, flags);
}
+
+ /**
+ * Sets a Context object that will be returned as the result of {@link #createPackageContext}
+ * for a specific {@code packageName}.
+ */
+ public void prepareCreatePackageContext(String packageName, Context context) {
+ mContextForPackage.put(packageName, context);
+ }
+
+ @Override
+ public Context createPackageContext(String packageName, int flags)
+ throws PackageManager.NameNotFoundException {
+ Context packageContext = mContextForPackage.get(packageName);
+ if (packageContext != null) {
+ return packageContext;
+ }
+ return super.createPackageContext(packageName, flags);
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/FakeReduceBrightColorsController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/FakeReduceBrightColorsController.kt
index e02042d..ab745ef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/FakeReduceBrightColorsController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/FakeReduceBrightColorsController.kt
@@ -47,14 +47,6 @@
}
}
- override fun setReduceBrightColorsFeatureAvailable(enabled: Boolean) {
- // do nothing
- }
-
- override fun isReduceBrightColorsFeatureAvailable(): Boolean {
- return true
- }
-
override fun isInUpgradeMode(resources: Resources?): Boolean {
if (resources != null) {
return Flags.evenDimmer() &&
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt
similarity index 73%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt
index 60d97d1..5c39e32 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.bouncer.shared.flag
+package com.android.systemui.accessibility.data.repository
import com.android.systemui.kosmos.Kosmos
-var Kosmos.fakeComposeBouncerFlags by Kosmos.Fixture { FakeComposeBouncerFlags() }
-val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
+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/accessibility/data/repository/FakeCaptioningRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeCaptioningRepository.kt
new file mode 100644
index 0000000..a639463
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeCaptioningRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.accessibility.data.model.CaptioningModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeCaptioningRepository : CaptioningRepository {
+
+ private val mutableCaptioningModel = MutableStateFlow<CaptioningModel?>(null)
+ override val captioningModel: StateFlow<CaptioningModel?> = mutableCaptioningModel.asStateFlow()
+
+ override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) {
+ mutableCaptioningModel.value =
+ CaptioningModel(
+ isSystemAudioCaptioningEnabled = isEnabled,
+ isSystemAudioCaptioningUiEnabled =
+ mutableCaptioningModel.value?.isSystemAudioCaptioningUiEnabled == true,
+ )
+ }
+
+ fun setIsSystemAudioCaptioningUiEnabled(isEnabled: Boolean) {
+ mutableCaptioningModel.value =
+ CaptioningModel(
+ isSystemAudioCaptioningEnabled =
+ mutableCaptioningModel.value?.isSystemAudioCaptioningEnabled == true,
+ isSystemAudioCaptioningUiEnabled = isEnabled,
+ )
+ }
+}
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/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt
index 9bf4756..15c7e25 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-package com.android.systemui.shade.ui.viewmodel
+package com.android.systemui.biometrics.domain.interactor
+import com.android.systemui.keyguard.domain.interactor.deviceEntrySideFpsOverlayInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
-val Kosmos.notificationsShadeSceneActionsViewModel:
- NotificationsShadeSceneActionsViewModel by Fixture {
- NotificationsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
+@OptIn(ExperimentalCoroutinesApi::class)
+val Kosmos.sideFpsOverlayInteractor by Fixture {
+ SideFpsOverlayInteractorImpl(
+ biometricStatusInteractor,
+ displayStateInteractor,
+ deviceEntrySideFpsOverlayInteractor,
+ sideFpsSensorInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
index 79d58a1..59809e3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
@@ -19,27 +19,19 @@
import android.content.applicationContext
import android.view.layoutInflater
import android.view.windowManager
-import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
-import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
-import com.android.systemui.biometrics.domain.interactor.sideFpsSensorInteractor
-import com.android.systemui.keyguard.domain.interactor.deviceEntrySideFpsOverlayInteractor
-import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
+import com.android.systemui.biometrics.domain.interactor.sideFpsOverlayInteractor
+import com.android.systemui.biometrics.ui.viewmodel.sideFpsOverlayViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.sideFpsOverlayViewBinder by Fixture {
SideFpsOverlayViewBinder(
- applicationScope = applicationCoroutineScope,
- applicationContext = applicationContext,
- { biometricStatusInteractor },
- { displayStateInteractor },
- { deviceEntrySideFpsOverlayInteractor },
+ applicationCoroutineScope,
+ applicationContext,
{ layoutInflater },
- { sideFpsProgressBarViewModel },
- { sideFpsSensorInteractor },
+ { sideFpsOverlayInteractor },
+ { sideFpsOverlayViewModel },
{ windowManager }
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
index de03855..e10b2dd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
@@ -27,9 +27,9 @@
@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.sideFpsOverlayViewModel by Fixture {
SideFpsOverlayViewModel(
- applicationContext = applicationContext,
- deviceEntrySideFpsOverlayInteractor = deviceEntrySideFpsOverlayInteractor,
- displayStateInteractor = displayStateInteractor,
- sfpsSensorInteractor = sideFpsSensorInteractor,
+ applicationContext,
+ deviceEntrySideFpsOverlayInteractor,
+ displayStateInteractor,
+ sideFpsSensorInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
index 5ced578..3087d01 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
@@ -26,6 +26,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.telephony.domain.interactor.telephonyInteractor
import com.android.systemui.user.domain.interactor.selectedUserInteractor
@@ -50,5 +51,6 @@
},
metricsLogger = metricsLogger,
dozeLogger = mock(),
+ sceneInteractor = { sceneInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
deleted file mode 100644
index 7482c0f..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.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.systemui.bouncer.shared.flag
-
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-
-class FakeComposeBouncerFlags(var composeBouncerEnabled: Boolean = false) : ComposeBouncerFlags {
- override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
- return SceneContainerFlag.isEnabled || composeBouncerEnabled
- }
-
- @Deprecated(
- "Avoid using this, this is meant to be used only by the glue code " +
- "that includes compose bouncer in legacy keyguard.",
- replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
- )
- override fun isOnlyComposeBouncerEnabled(): Boolean = composeBouncerEnabled
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
index e8612d08..5c5969d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
@@ -22,7 +22,6 @@
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
-import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
import com.android.systemui.deviceentry.domain.interactor.biometricMessageInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
@@ -45,7 +44,6 @@
faceAuthInteractor = deviceEntryFaceAuthInteractor,
deviceUnlockedInteractor = deviceUnlockedInteractor,
deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
- flags = composeBouncerFlags,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index e405d17..649e4e8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -25,7 +25,6 @@
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
-import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -34,16 +33,16 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.StateFlow
-val Kosmos.bouncerSceneActionsViewModel by Fixture {
- BouncerSceneActionsViewModel(
+val Kosmos.bouncerUserActionsViewModel by Fixture {
+ BouncerUserActionsViewModel(
bouncerInteractor = bouncerInteractor,
)
}
-val Kosmos.bouncerSceneActionsViewModelFactory by Fixture {
- object : BouncerSceneActionsViewModel.Factory {
- override fun create(): BouncerSceneActionsViewModel {
- return bouncerSceneActionsViewModel
+val Kosmos.bouncerUserActionsViewModelFactory by Fixture {
+ object : BouncerUserActionsViewModel.Factory {
+ override fun create(): BouncerUserActionsViewModel {
+ return bouncerUserActionsViewModel
}
}
}
@@ -55,7 +54,6 @@
authenticationInteractor = authenticationInteractor,
devicePolicyManager = devicePolicyManager,
bouncerMessageViewModelFactory = bouncerMessageViewModelFactory,
- flags = composeBouncerFlags,
userSwitcher = userSwitcherViewModel,
actionButtonInteractor = bouncerActionButtonInteractor,
pinViewModelFactory = pinBouncerViewModelFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
index 86a8ae5..1ef3464 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
@@ -17,11 +17,8 @@
package com.android.systemui.common.ui
import android.content.applicationContext
-import android.view.layoutInflater
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.policy.configurationController
val Kosmos.configurationState: ConfigurationState by
- Kosmos.Fixture {
- ConfigurationState(configurationController, applicationContext, layoutInflater)
- }
+ Kosmos.Fixture { ConfigurationStateImpl(configurationController, applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 4ad046c..629fda6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -36,6 +36,7 @@
import com.android.systemui.plugins.activityStarter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.settings.userTracker
+import com.android.systemui.statusbar.phone.fakeManagedProfileController
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
@@ -61,6 +62,7 @@
sceneInteractor = sceneInteractor,
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
+ managedProfileController = fakeManagedProfileController
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
index caa6e99..13116e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
@@ -19,6 +19,7 @@
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -34,5 +35,6 @@
sceneInteractor = sceneInteractor,
deviceUnlockedInteractor = deviceUnlockedInteractor,
alternateBouncerInteractor = alternateBouncerInteractor,
+ dismissCallbackRegistry = dismissCallbackRegistry,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
index 811c653..80f6fc2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
@@ -19,6 +19,7 @@
import android.hardware.input.InputManager
import com.android.systemui.education.data.repository.fakeEduClock
import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository
+import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
@@ -50,6 +51,15 @@
Kosmos.Fixture {
KeyboardTouchpadEduStatsInteractorImpl(
backgroundScope = testScope.backgroundScope,
- contextualEducationInteractor = contextualEducationInteractor
+ contextualEducationInteractor = contextualEducationInteractor,
+ inputDeviceRepository =
+ UserInputDeviceRepository(
+ testDispatcher,
+ keyboardRepository,
+ touchpadRepository,
+ userRepository
+ ),
+ tutorialSchedulerRepository,
+ fakeEduClock
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt
index 434953f..ff71f2f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt
@@ -17,5 +17,7 @@
package com.android.systemui.haptics
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.VibratorHelper
-var Kosmos.vibratorHelper by Kosmos.Fixture { FakeVibratorHelper() }
+var Kosmos.vibratorHelper: VibratorHelper by Kosmos.Fixture { fakeVibratorHelper }
+val Kosmos.fakeVibratorHelper by Kosmos.Fixture { FakeVibratorHelper() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt
index f5a05b4..4f5c32a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt
@@ -17,5 +17,7 @@
package com.android.systemui.haptics.msdl
import com.android.systemui.kosmos.Kosmos
+import com.google.android.msdl.domain.MSDLPlayer
-val Kosmos.msdlPlayer by Kosmos.Fixture { FakeMSDLPlayer() }
+var Kosmos.msdlPlayer: MSDLPlayer by Kosmos.Fixture { fakeMSDLPlayer }
+val Kosmos.fakeMSDLPlayer by Kosmos.Fixture { FakeMSDLPlayer() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt
index 827f0d2..a83baff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt
@@ -16,8 +16,20 @@
package com.android.systemui.inputdevice.tutorial
+import android.content.applicationContext
+import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
import org.mockito.kotlin.mock
var Kosmos.inputDeviceTutorialLogger: InputDeviceTutorialLogger by
Kosmos.Fixture { mock<InputDeviceTutorialLogger>() }
+
+var Kosmos.tutorialSchedulerRepository by
+ Kosmos.Fixture {
+ TutorialSchedulerRepository(
+ applicationContext = applicationContext,
+ testScope.backgroundScope,
+ "KosmosTutorialSchedulerRepository"
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index e96aeada..5753c6c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -27,7 +27,6 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
@SysUISingleton
@@ -37,38 +36,41 @@
private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null)
override val authenticationStatus: Flow<FaceAuthenticationStatus> =
_authenticationStatus.filterNotNull()
+
fun setAuthenticationStatus(status: FaceAuthenticationStatus) {
_authenticationStatus.value = status
}
+
private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
override val detectionStatus: Flow<FaceDetectionStatus>
get() = _detectionStatus.filterNotNull()
+
fun setDetectionStatus(status: FaceDetectionStatus) {
_detectionStatus.value = status
}
private val _isLockedOut = MutableStateFlow(false)
override val isLockedOut = _isLockedOut
- private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null)
- val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> =
- _runningAuthRequest.asStateFlow()
+ val runningAuthRequest: MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?> =
+ MutableStateFlow(null)
private val _isAuthRunning = MutableStateFlow(false)
override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning
override val isBypassEnabled = MutableStateFlow(false)
+
override fun setLockedOut(isLockedOut: Boolean) {
_isLockedOut.value = isLockedOut
}
override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
- _runningAuthRequest.value = uiEvent to fallbackToDetection
+ runningAuthRequest.value = uiEvent to fallbackToDetection
_isAuthRunning.value = true
}
override fun cancel() {
_isAuthRunning.value = false
- _runningAuthRequest.value = null
+ runningAuthRequest.value = null
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
index 27eadb1..d920c4f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
@@ -25,6 +25,7 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.notifShadeSceneFamilyResolver
import com.android.systemui.scene.domain.resolver.quickSettingsSceneFamilyResolver
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ExperimentalCoroutinesApi
@@ -35,11 +36,13 @@
transitionInteractor = keyguardTransitionInteractor,
dismissInteractor = keyguardDismissInteractor,
applicationScope = testScope.backgroundScope,
- sceneInteractor = sceneInteractor,
- deviceEntryInteractor = deviceEntryInteractor,
- quickSettingsSceneFamilyResolver = quickSettingsSceneFamilyResolver,
- notifShadeSceneFamilyResolver = notifShadeSceneFamilyResolver,
+ sceneInteractor = { sceneInteractor },
+ deviceEntryInteractor = { deviceEntryInteractor },
+ quickSettingsSceneFamilyResolver = { quickSettingsSceneFamilyResolver },
+ notifShadeSceneFamilyResolver = { notifShadeSceneFamilyResolver },
powerInteractor = powerInteractor,
alternateBouncerInteractor = alternateBouncerInteractor,
+ keyguardInteractor = { keyguardInteractor },
+ shadeInteractor = { shadeInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
index 1e95fc1..740d891 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerWindowViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.mockito.mock
@@ -64,6 +65,7 @@
},
messageAreaViewModel = mock<AlternateBouncerMessageAreaViewModel>(),
powerInteractor = powerInteractor,
+ touchLogBuffer = logcatLogBuffer(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index b9443bc..7cf4083 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -25,6 +25,7 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -39,6 +40,7 @@
communalInteractor = communalInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
notificationsKeyguardInteractor = notificationsKeyguardInteractor,
+ notificationShadeWindowModel = notificationShadeWindowModel,
alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
alternateBouncerToLockscreenTransitionViewModel =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt
index 9bf4756..a25b29fd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.shade.ui.viewmodel
+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.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
import com.android.systemui.shade.domain.interactor.shadeInteractor
-val Kosmos.notificationsShadeSceneActionsViewModel:
- NotificationsShadeSceneActionsViewModel by Fixture {
- NotificationsShadeSceneActionsViewModel(
+val Kosmos.lockscreenUserActionsViewModel by Fixture {
+ LockscreenUserActionsViewModel(
+ deviceEntryInteractor = deviceEntryInteractor,
+ communalInteractor = communalInteractor,
shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index b34681a..f8df707 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -5,9 +5,12 @@
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
+var Kosmos.unconfinedTestDispatcher by Fixture { UnconfinedTestDispatcher() }
var Kosmos.testScope by Fixture { TestScope(testDispatcher) }
+var Kosmos.unconfinedTestScope by Fixture { TestScope(unconfinedTestDispatcher) }
var Kosmos.applicationCoroutineScope by Fixture { testScope.backgroundScope }
var Kosmos.testCase: SysuiTestCase by Fixture()
var Kosmos.backgroundCoroutineContext: CoroutineContext by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 953363d..c60305e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -36,6 +36,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.globalactions.domain.interactor.globalActionsInteractor
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.haptics.qs.qsLongPressEffect
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -60,6 +61,7 @@
import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.shade.shadeController
import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
@@ -154,4 +156,6 @@
val scrimController by lazy { kosmos.scrimController }
val scrimStartable by lazy { kosmos.scrimStartable }
val sceneContainerOcclusionInteractor by lazy { kosmos.sceneContainerOcclusionInteractor }
+ val msdlPlayer by lazy { kosmos.fakeMSDLPlayer }
+ val shadeModeInteractor by lazy { kosmos.shadeModeInteractor }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferFactoryKosmos.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferFactoryKosmos.kt
index 5d70ed6..c55dc6a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferFactoryKosmos.kt
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.log.table
+import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.log.LogcatEchoTrackerAlways
+import com.android.systemui.util.time.fakeSystemClock
-val Kosmos.quickSettingsShadeSceneActionsViewModel: QuickSettingsShadeSceneActionsViewModel by
+val Kosmos.tableLogBufferFactory: TableLogBufferFactory by
Kosmos.Fixture {
- QuickSettingsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
- quickSettingsContainerViewModel = quickSettingsContainerViewModel,
+ TableLogBufferFactory(
+ dumpManager = dumpManager,
+ systemClock = fakeSystemClock,
+ logcatEchoTracker = LogcatEchoTrackerAlways(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferHelper.kt
new file mode 100644
index 0000000..64c3974
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferHelper.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.table
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.LogcatEchoTrackerAlways
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.time.fakeSystemClock
+
+/**
+ * Creates a [TableLogBuffer] that will echo everything to logcat, which is useful for debugging
+ * tests.
+ */
+fun logcatTableLogBuffer(kosmos: Kosmos, name: String = "EchoToLogcatTableLogBuffer") =
+ logcatTableLogBuffer(kosmos.fakeSystemClock, name)
+
+/**
+ * Creates a [TableLogBuffer] that will echo everything to logcat, which is useful for debugging
+ * tests.
+ */
+fun logcatTableLogBuffer(systemClock: SystemClock, name: String = "EchoToLogcatTableLogBuffer") =
+ TableLogBuffer(maxSize = 50, name, systemClock, logcatEchoTracker = LogcatEchoTrackerAlways())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt
index a5690a0..cb7750f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt
@@ -24,7 +24,6 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.util.fakeMediaControllerFactory
import com.android.systemui.media.controls.util.mediaFlags
-import com.android.systemui.plugins.activityStarter
val Kosmos.mediaDataLoader by
Kosmos.Fixture {
@@ -32,7 +31,6 @@
testableContext,
testDispatcher,
testScope,
- activityStarter,
fakeMediaControllerFactory,
mediaFlags,
imageLoader,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt
index 632436a..174e653 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt
@@ -26,6 +26,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.media.controls.data.repository.mediaDataRepository
+import com.android.systemui.media.controls.shared.mediaLogger
import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.util.fakeMediaControllerFactory
import com.android.systemui.media.controls.util.mediaFlags
@@ -60,5 +61,6 @@
keyguardUpdateMonitor = keyguardUpdateMonitor,
mediaDataRepository = mediaDataRepository,
mediaDataLoader = { mediaDataLoader },
+ mediaLogger = mediaLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerKosmos.kt
index b7660e0..b33edf9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerKosmos.kt
@@ -28,6 +28,8 @@
Kosmos.Fixture {
MediaTimeoutListener(
mediaControllerFactory = fakeMediaControllerFactory,
+ bgExecutor = fakeExecutor,
+ uiExecutor = fakeExecutor,
mainExecutor = fakeExecutor,
logger = MediaTimeoutLogger(logcatLogBuffer("MediaTimeoutLogBuffer")),
statusBarStateController = statusBarStateController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt
index a0fc76b..4978558 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt
@@ -24,6 +24,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.fakeSystemClock
val Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by
Kosmos.Fixture {
@@ -39,6 +40,7 @@
activityManager,
mock(),
fakeExecutor,
+ fakeSystemClock,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
index b03542c..33227a4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import android.content.applicationContext
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.qs.panels.domain.interactor.editTilesListInteractor
@@ -33,6 +35,8 @@
currentTilesInteractor,
tilesAvailabilityInteractor,
minimumTilesInteractor,
+ configurationInteractor,
+ applicationContext,
infiniteGridLayout,
applicationCoroutineScope,
gridLayoutTypeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
index dceb8bf..f66125a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.instanceIdSequenceFake
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
@@ -47,6 +48,7 @@
tileSpec,
QSTileUIConfig.Empty,
instanceIdSequenceFake.newInstanceId(),
+ category = TileCategory.PROVIDED_BY_APP,
)
object : QSTileViewModel {
override val state: StateFlow<QSTileState?> =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
index 2a0ee88..73d9b32 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
@@ -18,6 +18,7 @@
import com.android.internal.logging.InstanceId
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
object QSTileConfigTestBuilder {
@@ -30,12 +31,14 @@
var instanceId: InstanceId = InstanceId.fakeInstanceId(0)
var metricsSpec: String = tileSpec.spec
var policy: QSTilePolicy = QSTilePolicy.NoRestrictions
+ var category: TileCategory = TileCategory.UNKNOWN
fun build() =
QSTileConfig(
tileSpec,
uiConfig,
instanceId,
+ category,
metricsSpec,
policy,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
index 60d97d1..8fc40e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.bouncer.shared.flag
+package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
-var Kosmos.fakeComposeBouncerFlags by Kosmos.Fixture { FakeComposeBouncerFlags() }
-val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
+val Kosmos.quickSettingsShadeOverlayActionsViewModel:
+ QuickSettingsShadeOverlayActionsViewModel by Fixture {
+ QuickSettingsShadeOverlayActionsViewModel()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
index 5d70ed6..ff8b478 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
@@ -17,12 +17,14 @@
package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
-val Kosmos.quickSettingsShadeSceneActionsViewModel: QuickSettingsShadeSceneActionsViewModel by
+val Kosmos.quickSettingsShadeOverlayContentViewModel: QuickSettingsShadeOverlayContentViewModel by
Kosmos.Fixture {
- QuickSettingsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
+ QuickSettingsShadeOverlayContentViewModel(
+ sceneInteractor = sceneInteractor,
+ shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
quickSettingsContainerViewModel = quickSettingsContainerViewModel,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
index 5ad5cb2..cd1704c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
@@ -14,18 +14,13 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.ui.viewmodel.overlayShadeViewModelFactory
-import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.quickSettingsShadeSceneContentViewModel: QuickSettingsShadeSceneContentViewModel by
Kosmos.Fixture {
QuickSettingsShadeSceneContentViewModel(
- overlayShadeViewModelFactory = overlayShadeViewModelFactory,
quickSettingsContainerViewModel = quickSettingsContainerViewModel,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
similarity index 76%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
index 5d70ed6..06592b1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
@@ -17,12 +17,10 @@
package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-val Kosmos.quickSettingsShadeSceneActionsViewModel: QuickSettingsShadeSceneActionsViewModel by
+val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
Kosmos.Fixture {
- QuickSettingsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
+ QuickSettingsShadeUserActionsViewModel(
quickSettingsContainerViewModel = quickSettingsContainerViewModel,
)
}
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 7dfe802..8744638 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,12 +1,20 @@
package com.android.systemui.scene
-import com.android.compose.animation.scene.OverlayKey
+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.scene.shared.model.FakeScene
+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 com.android.systemui.scene.ui.viewmodel.splitEdgeDetector
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import kotlinx.coroutines.flow.MutableStateFlow
var Kosmos.sceneKeys by Fixture {
listOf(
@@ -19,19 +27,18 @@
)
}
-val Kosmos.fakeScenes by Fixture { sceneKeys.map { key -> FakeScene(key) }.toSet() }
-
-val Kosmos.scenes by Fixture { fakeScenes }
-
val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen }
var Kosmos.overlayKeys by Fixture {
- listOf<OverlayKey>(
- // TODO(b/356596436): Add overlays here when we have them.
+ listOf(
+ Overlays.NotificationsShade,
+ Overlays.QuickSettingsShade,
)
}
-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 }
@@ -53,3 +60,22 @@
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,
+ shadeInteractor = shadeInteractor,
+ splitEdgeDetector = splitEdgeDetector,
+ motionEventHandlerReceiver = {},
+ logger = sceneLogger
+ )
+ .apply { setTransitionState(transitionState) }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
index 59f2b94..c95b2dc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
@@ -29,7 +29,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
private val mutableTransitionState =
MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(Scenes.Lockscreen))
@@ -116,13 +115,13 @@
is ObservableTransitionState.Transition -> {
TransitionStep(
from =
- if (sceneTransition.fromScene != Scenes.Lockscreen) {
+ if (sceneTransition.fromContent != Scenes.Lockscreen) {
KeyguardState.UNDEFINED
} else {
state.from
},
to =
- if (sceneTransition.toScene != Scenes.Lockscreen) {
+ if (sceneTransition.toContent != Scenes.Lockscreen) {
KeyguardState.UNDEFINED
} else {
state.from
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
index ae33aea..d17b575 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
@@ -24,7 +24,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.sceneFamilyResolvers: Map<SceneKey, SceneResolver>
@@ -48,7 +48,7 @@
Kosmos.Fixture {
NotifShadeSceneFamilyResolver(
applicationScope = applicationCoroutineScope,
- shadeInteractor = shadeInteractor,
+ shadeModeInteractor = shadeModeInteractor,
)
}
@@ -56,6 +56,6 @@
Kosmos.Fixture {
QuickSettingsSceneFamilyResolver(
applicationScope = applicationCoroutineScope,
- shadeInteractor = shadeInteractor,
+ shadeModeInteractor = shadeModeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 53b6a2e..9a5698c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -24,8 +24,11 @@
import com.android.systemui.classifier.falsingCollector
import com.android.systemui.classifier.falsingManager
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.haptics.msdl.msdlPlayer
+import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -55,6 +58,7 @@
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
deviceEntryInteractor = deviceEntryInteractor,
+ deviceEntryHapticsInteractor = deviceEntryHapticsInteractor,
deviceUnlockedInteractor = deviceUnlockedInteractor,
bouncerInteractor = bouncerInteractor,
keyguardInteractor = keyguardInteractor,
@@ -82,5 +86,7 @@
dismissCallbackRegistry = dismissCallbackRegistry,
statusBarStateController = sysuiStatusBarStateController,
alternateBouncerInteractor = alternateBouncerInteractor,
+ vibratorHelper = vibratorHelper,
+ msdlPlayer = msdlPlayer,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
deleted file mode 100644
index 78358f5..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
+++ /dev/null
@@ -1,49 +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.scene.shared.model
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.onCompletion
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.receiveAsFlow
-
-class FakeScene(
- override val key: SceneKey,
-) : ExclusiveActivatable(), Scene {
- var isDestinationScenesBeingCollected = false
-
- private val destinationScenesChannel = Channel<Map<UserAction, UserActionResult>>()
-
- override val destinationScenes =
- destinationScenesChannel
- .receiveAsFlow()
- .onStart { isDestinationScenesBeingCollected = true }
- .onCompletion { isDestinationScenesBeingCollected = false }
-
- override suspend fun onActivated(): Nothing {
- awaitCancellation()
- }
-
- suspend fun setDestinationScenes(value: Map<UserAction, UserActionResult>) {
- destinationScenesChannel.send(value)
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorKosmos.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorKosmos.kt
index 5d70ed6..e0b5292 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorKosmos.kt
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.scene.ui.viewmodel
+import androidx.compose.ui.unit.dp
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.shade.domain.interactor.shadeInteractor
-val Kosmos.quickSettingsShadeSceneActionsViewModel: QuickSettingsShadeSceneActionsViewModel by
+var Kosmos.splitEdgeDetector: SplitEdgeDetector by
Kosmos.Fixture {
- QuickSettingsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
- quickSettingsContainerViewModel = quickSettingsContainerViewModel,
+ SplitEdgeDetector(
+ topEdgeSplitFraction = shadeInteractor::getTopEdgeSplitFraction,
+ edgeSize = 40.dp,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 4660337..4a86fd5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -72,10 +72,6 @@
@Deprecated("Use ShadeInteractor.isUserInteractingWithShade instead")
override val legacyLockscreenShadeTracking = MutableStateFlow(false)
- private var _isDualShadeAlignedToBottom = false
- override val isDualShadeAlignedToBottom
- get() = _isDualShadeAlignedToBottom
-
private var _isShadeLayoutWide = MutableStateFlow(false)
override val isShadeLayoutWide: StateFlow<Boolean> = _isShadeLayoutWide.asStateFlow()
@@ -155,10 +151,6 @@
_legacyShadeExpansion.value = expandedFraction
}
- fun setDualShadeAlignedToBottom(isAlignedToBottom: Boolean) {
- _isDualShadeAlignedToBottom = isAlignedToBottom
- }
-
override fun setShadeLayoutWide(isShadeLayoutWide: Boolean) {
_isShadeLayoutWide.value = isShadeLayoutWide
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index 54208b9..04d930c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -53,7 +53,7 @@
scope = applicationCoroutineScope,
keyguardRepository = keyguardRepository,
sharedNotificationContainerInteractor = sharedNotificationContainerInteractor,
- repository = shadeRepository
+ repository = shadeRepository,
)
}
var Kosmos.shadeInteractor: ShadeInteractor by Kosmos.Fixture { shadeInteractorImpl }
@@ -70,6 +70,6 @@
userSetupRepository = userSetupRepository,
userSwitcherInteractor = userSwitcherInteractor,
baseShadeInteractor = baseShadeInteractor,
- shadeRepository = shadeRepository,
+ shadeModeInteractor = shadeModeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
index 9bf4756..7892e96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.shade.ui.viewmodel
+package com.android.systemui.shade.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.data.repository.shadeRepository
-val Kosmos.notificationsShadeSceneActionsViewModel:
- NotificationsShadeSceneActionsViewModel by Fixture {
- NotificationsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
+val Kosmos.shadeModeInteractor by Fixture {
+ ShadeModeInteractorImpl(
+ applicationScope = applicationCoroutineScope,
+ repository = shadeRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt
similarity index 73%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt
index 9bf4756..1e00ac4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt
@@ -18,12 +18,9 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
-val Kosmos.notificationsShadeSceneActionsViewModel:
- NotificationsShadeSceneActionsViewModel by Fixture {
- NotificationsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
- )
+val Kosmos.notificationsShadeOverlayActionsViewModel:
+ NotificationsShadeOverlayActionsViewModel by Fixture {
+ NotificationsShadeOverlayActionsViewModel()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
new file mode 100644
index 0000000..9cdd519
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.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.shade.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory
+
+val Kosmos.notificationsShadeOverlayContentViewModel:
+ NotificationsShadeOverlayContentViewModel by Fixture {
+ NotificationsShadeOverlayContentViewModel(
+ shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
+ notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory,
+ sceneInteractor = sceneInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt
index 9bf4756..6345c40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt
@@ -18,12 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeUserActionsViewModel
-val Kosmos.notificationsShadeSceneActionsViewModel:
- NotificationsShadeSceneActionsViewModel by Fixture {
- NotificationsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
- )
-}
+val Kosmos.notificationsShadeUserActionsViewModel:
+ NotificationsShadeUserActionsViewModel by Fixture { NotificationsShadeUserActionsViewModel() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
index 4b42e07..9742595 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
@@ -16,12 +16,18 @@
package com.android.systemui.shade.ui.viewmodel
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
val Kosmos.notificationShadeWindowModel: NotificationShadeWindowModel by
Kosmos.Fixture {
NotificationShadeWindowModel(
keyguardTransitionInteractor,
+ sceneInteractor = { sceneInteractor },
+ authenticationInteractor = { authenticationInteractor },
+ primaryBouncerInteractor = primaryBouncerInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
deleted file mode 100644
index 00f1526..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
+++ /dev/null
@@ -1,38 +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.shade.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-
-val Kosmos.overlayShadeViewModel: OverlayShadeViewModel by
- Kosmos.Fixture {
- OverlayShadeViewModel(
- sceneInteractor = sceneInteractor,
- shadeInteractor = shadeInteractor,
- )
- }
-
-val Kosmos.overlayShadeViewModelFactory: OverlayShadeViewModel.Factory by
- Kosmos.Fixture {
- object : OverlayShadeViewModel.Factory {
- override fun create(): OverlayShadeViewModel {
- return overlayShadeViewModel
- }
- }
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelKosmos.kt
similarity index 89%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelKosmos.kt
index 2387aa8..48c5121 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelKosmos.kt
@@ -21,8 +21,8 @@
import com.android.systemui.qs.ui.adapter.qsSceneAdapter
import com.android.systemui.shade.domain.interactor.shadeInteractor
-val Kosmos.shadeSceneActionsViewModel: ShadeSceneActionsViewModel by Fixture {
- ShadeSceneActionsViewModel(
+val Kosmos.shadeUserActionsViewModel: ShadeUserActionsViewModel by Fixture {
+ ShadeUserActionsViewModel(
qsSceneAdapter = qsSceneAdapter,
shadeInteractor = shadeInteractor,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index 634354b..8bfc390 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -35,3 +35,11 @@
dumpManager = dumpManager,
)
}
+
+val Kosmos.notificationsPlaceholderViewModelFactory by Fixture {
+ object : NotificationsPlaceholderViewModel.Factory {
+ override fun create(): NotificationsPlaceholderViewModel {
+ return notificationsPlaceholderViewModel
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.kt
index 9bf4756..ef04b9d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.kt
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.shade.ui.viewmodel
+@file:OptIn(ExperimentalCoroutinesApi::class)
+package com.android.systemui.statusbar.phone
+
+import android.testing.LeakCheck
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.utils.leaks.FakeManagedProfileController
+import kotlinx.coroutines.ExperimentalCoroutinesApi
-val Kosmos.notificationsShadeSceneActionsViewModel:
- NotificationsShadeSceneActionsViewModel by Fixture {
- NotificationsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
- )
-}
+val Kosmos.fakeManagedProfileController by Fixture { FakeManagedProfileController(LeakCheck()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
index 386c7c5..d76defe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
@@ -18,7 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
val Kosmos.airplaneModeInteractor: AirplaneModeInteractor by
@@ -26,6 +26,6 @@
AirplaneModeInteractor(
FakeAirplaneModeRepository(),
FakeConnectivityRepository(),
- FakeMobileConnectionsRepository(),
+ fakeMobileConnectionsRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 8229575..de73d33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -23,18 +23,16 @@
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
-import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
// TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionsRepository
class FakeMobileConnectionsRepository(
mobileMappings: MobileMappingsProxy = FakeMobileMappingsProxy(),
- val tableLogBuffer: TableLogBuffer = mock<TableLogBuffer> {},
+ val tableLogBuffer: TableLogBuffer,
) : MobileConnectionsRepository {
val GSM_KEY = mobileMappings.toIconKey(GSM)
@@ -94,9 +92,10 @@
private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
override val defaultMobileIconGroup = _defaultMobileIconGroup
- override val deviceServiceState = MutableStateFlow<ServiceStateModel?>(null)
+ override val isDeviceEmergencyCallCapable = MutableStateFlow(false)
override val isAnySimSecure = MutableStateFlow(false)
+
override fun getIsAnySimSecure(): Boolean = isAnySimSecure.value
private var isInEcmMode: Boolean = false
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
index 9d62ea5..cd22f1d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
@@ -18,10 +18,12 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.log.table.logcatTableLogBuffer
val Kosmos.fakeMobileConnectionsRepository by Fixture {
- FakeMobileConnectionsRepository(tableLogBuffer = mock())
+ FakeMobileConnectionsRepository(
+ tableLogBuffer = logcatTableLogBuffer(this, "FakeMobileConnectionsRepository"),
+ )
}
val Kosmos.mobileConnectionsRepository by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 709be5e..7ca90ea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -32,7 +32,7 @@
override val isWifiDefault: StateFlow<Boolean> = _isWifiDefault
private val _wifiNetwork: MutableStateFlow<WifiNetworkModel> =
- MutableStateFlow(WifiNetworkModel.Inactive)
+ MutableStateFlow(WifiNetworkModel.Inactive())
override val wifiNetwork: StateFlow<WifiNetworkModel> = _wifiNetwork
override val secondaryNetworks = MutableStateFlow<List<WifiNetworkModel>>(emptyList())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
index 66be7e7..61b53c9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.statusbar.policy.domain.interactor
import android.content.testableContext
+import com.android.settingslib.notification.modes.zenIconLoader
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.shared.notifications.data.repository.notificationSettingsRepository
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
@@ -27,5 +29,7 @@
context = testableContext,
zenModeRepository = zenModeRepository,
notificationSettingsRepository = notificationSettingsRepository,
+ bgDispatcher = testDispatcher,
+ iconLoader = zenIconLoader,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/utils/FakeUserScopedService.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/utils/FakeUserScopedService.kt
new file mode 100644
index 0000000..78763f9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/utils/FakeUserScopedService.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.user.utils
+
+import android.os.UserHandle
+
+class FakeUserScopedService<T>(private val defaultImplementation: T) : UserScopedService<T> {
+
+ private val implementations = mutableMapOf<UserHandle, T>()
+
+ fun addImplementation(user: UserHandle, implementation: T) {
+ implementations[user] = implementation
+ }
+
+ fun removeImplementation(user: UserHandle): T? = implementations.remove(user)
+
+ override fun forUser(user: UserHandle): T =
+ implementations.getOrDefault(user, defaultImplementation)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
index 35fa2af..73d423c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -19,5 +19,10 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.unconfinedTestDispatcher
val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings(testDispatcher) }
+
+val Kosmos.unconfinedDispatcherFakeGlobalSettings: FakeGlobalSettings by Fixture {
+ FakeGlobalSettings(unconfinedTestDispatcher)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
index 76ef202..e1daf9b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
@@ -19,8 +19,13 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.unconfinedTestDispatcher
import com.android.systemui.settings.userTracker
val Kosmos.fakeSettings: FakeSettings by Fixture {
FakeSettings(testDispatcher) { userTracker.userId }
}
+
+val Kosmos.unconfinedDispatcherFakeSettings: FakeSettings by Fixture {
+ FakeSettings(unconfinedTestDispatcher) { userTracker.userId }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 2dbac67..0089199 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -64,7 +64,8 @@
@Override
public void setResourceIcon(String slot, @Nullable String resPackage, int iconResId,
- @Nullable Drawable preloadedIcon, CharSequence contentDescription) {
+ @Nullable Drawable preloadedIcon, CharSequence contentDescription,
+ StatusBarIcon.Shape shape) {
}
@Override
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/view/accessibility/data/repository/FakeCaptioningRepository.kt
deleted file mode 100644
index 663aaf2..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/FakeCaptioningRepository.kt
+++ /dev/null
@@ -1,41 +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.view.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
-
-class FakeCaptioningRepository : CaptioningRepository {
-
- private val mutableIsSystemAudioCaptioningEnabled = MutableStateFlow(false)
- override val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
- get() = mutableIsSystemAudioCaptioningEnabled.asStateFlow()
-
- private val mutableIsSystemAudioCaptioningUiEnabled = MutableStateFlow(false)
- override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
- get() = mutableIsSystemAudioCaptioningUiEnabled.asStateFlow()
-
- override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) {
- mutableIsSystemAudioCaptioningEnabled.value = isEnabled
- }
-
- fun setIsSystemAudioCaptioningUiEnabled(isSystemAudioCaptioningUiEnabled: Boolean) {
- mutableIsSystemAudioCaptioningUiEnabled.value = isSystemAudioCaptioningUiEnabled
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index 1fa6c3f..ba6ffd7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -33,6 +33,8 @@
class FakeAudioRepository : AudioRepository {
+ private val unMutableStreams = setOf(AudioManager.STREAM_VOICE_CALL, AudioManager.STREAM_ALARM)
+
private val mutableMode = MutableStateFlow(AudioManager.MODE_NORMAL)
override val mode: StateFlow<Int> = mutableMode.asStateFlow()
@@ -73,7 +75,7 @@
volume = 0,
minVolume = 0,
maxVolume = 10,
- isAffectedByMute = false,
+ isAffectedByMute = audioStream.value !in unMutableStreams,
isAffectedByRingerMode = false,
isMuted = false,
)
@@ -120,7 +122,7 @@
lastAudibleVolumes[audioStream] = volume
}
- override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) {
+ override suspend fun setRingerModeInternal(audioStream: AudioStream, mode: RingerMode) {
mutableRingerMode.value = mode
}
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/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
index 83adc79..ad1292e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
@@ -38,6 +38,7 @@
): MediaDevice = mock {
whenever(name).thenReturn(deviceName)
whenever(icon).thenReturn(deviceIcon)
+ whenever(deviceType).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE)
}
fun wiredMediaDevice(
@@ -77,6 +78,18 @@
whenever(name).thenReturn(deviceName)
whenever(icon).thenReturn(deviceIcon)
whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
+ whenever(deviceType).thenReturn(MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE)
+ }
+ }
+
+ fun remoteMediaDevice(
+ deviceName: String = "remote_media",
+ deviceIcon: Drawable? = TestStubDrawable(),
+ ): MediaDevice {
+ return mock<MediaDevice> {
+ whenever(name).thenReturn(deviceName)
+ whenever(icon).thenReturn(deviceIcon)
+ whenever(deviceType).thenReturn(MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE)
}
}
}
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/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 26b0f61..136738f 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -22,6 +22,7 @@
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
+import android.hardware.SyncFence;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraExtensionCharacteristics;
@@ -2525,6 +2526,19 @@
}
@Override
+ public SyncFence getFence() {
+ if (mParcelImage.fence != null) {
+ try {
+ return SyncFence.create(mParcelImage.fence.dup());
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parcel buffer fence!");
+ }
+ }
+
+ return SyncFence.createEmpty();
+ }
+
+ @Override
protected final void finalize() throws Throwable {
try {
close();
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index d6b25fc..3c65f37 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -94,6 +94,9 @@
libs: [
"ravenwood-runtime-common-ravenwood",
],
+ static_libs: [
+ "framework-annotations-lib", // should it be "libs" instead?
+ ],
visibility: ["//visibility:private"],
}
@@ -126,8 +129,9 @@
],
libs: [
"framework-minus-apex.ravenwood",
- "ravenwood-junit",
+ "ravenwood-helper-libcore-runtime",
],
+ sdk_version: "core_current",
visibility: ["//visibility:private"],
}
@@ -161,6 +165,8 @@
"services.core.ravenwood",
"junit",
"framework-annotations-lib",
+ "ravenwood-helper-framework-runtime",
+ "ravenwood-helper-libcore-runtime",
],
visibility: ["//frameworks/base"],
jarjar_rules: ":ravenwood-services-jarjar-rules",
diff --git a/ravenwood/OWNERS b/ravenwood/OWNERS
index badfca0..f7b13d1 100644
--- a/ravenwood/OWNERS
+++ b/ravenwood/OWNERS
@@ -1,8 +1,8 @@
set noparent
[email protected]
[email protected]
[email protected]
[email protected]
[email protected] #{LAST_RESORT_SUGGESTION}
+
per-file ravenwood-annotation-allowed-classes.txt = [email protected]
per-file texts/ravenwood-annotation-allowed-classes.txt = [email protected]
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index b73f235..7f9d9c2 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -2,13 +2,11 @@
"presubmit": [
{ "name": "tiny-framework-dump-test" },
{ "name": "hoststubgentest" },
+ { "name": "hoststubgen-test-tiny-test" },
{ "name": "hoststubgen-invoke-test" },
- {
- "name": "RavenwoodMockitoTest_device"
- },
- {
- "name": "RavenwoodBivalentTest_device"
- },
+ { "name": "RavenwoodMockitoTest_device" },
+ { "name": "RavenwoodBivalentTest_device" },
+
// The sysui tests should match vendor/unbundled_google/packages/SystemUIGoogle/TEST_MAPPING
{
"name": "SystemUIGoogleTests",
@@ -144,6 +142,10 @@
"host": true
},
{
+ "name": "RavenwoodCoreTest",
+ "host": true
+ },
+ {
"name": "RavenwoodMinimumTest",
"host": true
},
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRedirect.java
similarity index 82%
copy from ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
copy to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRedirect.java
index 4b9cf85..b582ccf 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRedirect.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -15,7 +15,7 @@
*/
package android.ravenwood.annotation;
-import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -29,8 +29,7 @@
*
* @hide
*/
-@Target({TYPE})
+@Target({METHOD})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodNativeSubstitutionClass {
- String value();
+public @interface RavenwoodRedirect {
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRedirectionClass.java
similarity index 94%
rename from ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRedirectionClass.java
index 4b9cf85..bee9222 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRedirectionClass.java
@@ -31,6 +31,6 @@
*/
@Target({TYPE})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodNativeSubstitutionClass {
+public @interface RavenwoodRedirectionClass {
String value();
}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
new file mode 100644
index 0000000..a5a16c1
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.ravenwoodtest.bivalenttest;
+
+import static android.platform.test.ravenwood.RavenwoodConfig.isOnRavenwood;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.ravenwood.RavenwoodConfig;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test to make sure the config field is used.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodConfigTest {
+ private static final String PACKAGE_NAME = "com.test";
+
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig =
+ new RavenwoodConfig.Builder()
+ .setPackageName(PACKAGE_NAME)
+ .build();
+
+ @Test
+ public void testConfig() {
+ assumeTrue(isOnRavenwood());
+ assertEquals(PACKAGE_NAME,
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageName());
+ }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java
new file mode 100644
index 0000000..c25d2b4
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ravenwoodtest.bivalenttest;
+
+import android.platform.test.ravenwood.RavenwoodConfig;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+/**
+ * Make sure having multiple RavenwoodRule's is detected.
+ * (But only when running on ravenwod. Otherwise it'll be ignored.)
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodMultipleRuleTest {
+
+ @Rule(order = Integer.MIN_VALUE)
+ public final ExpectedException mExpectedException = ExpectedException.none();
+
+ @Rule
+ public final RavenwoodRule mRavenwood1 = new RavenwoodRule();
+
+ @Rule
+ public final RavenwoodRule mRavenwood2 = new RavenwoodRule();
+
+ public RavenwoodMultipleRuleTest() {
+ // We can't call it within the test method because the exception happens before
+ // calling the method, so set it up here.
+ if (RavenwoodConfig.isOnRavenwood()) {
+ mExpectedException.expectMessage("Multiple nesting RavenwoodRule");
+ }
+ }
+
+ @Test
+ public void testMultipleRulesNotAllowed() {
+ Assume.assumeTrue(RavenwoodConfig.isOnRavenwood());
+ }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java
index 8dadd39..09a0aa8 100644
--- a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java
@@ -64,7 +64,7 @@
/**
* Check the number of calls stored in {@link #mNumCalled}.
*/
- protected void assertCalls(Object... methodNameAndCountPairs) {
+ public void assertCalls(Object... methodNameAndCountPairs) {
// Create a local copy
HashMap<String, Integer> counts = new HashMap<>(mNumCalled);
for (int i = 0; i < methodNameAndCountPairs.length - 1; i += 2) {
@@ -95,7 +95,7 @@
* Same as {@link #assertCalls(Object...)} but it kills the process if it fails.
* Only use in @AfterClass.
*/
- protected void assertCallsOrDie(Object... methodNameAndCountPairs) {
+ public void assertCallsOrDie(Object... methodNameAndCountPairs) {
try {
assertCalls(methodNameAndCountPairs);
} catch (Throwable th) {
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
new file mode 100644
index 0000000..9d878f4
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.ravenwoodtest.bivalenttest.ravenizer;
+
+import android.platform.test.annotations.NoRavenizer;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+
+import org.junit.Test;
+
+/**
+ * Test for {@link android.platform.test.annotations.NoRavenizer}
+ */
+@NoRavenizer
+public class RavenwoodNoRavenizerTest {
+ public static final String TAG = "RavenwoodNoRavenizerTest";
+
+ private static final CallTracker sCallTracker = new CallTracker();
+
+ /**
+ * With @NoRavenizer, this method shouldn't be called.
+ */
+ @RavenwoodTestRunnerInitializing
+ public static void ravenwoodRunnerInitializing() {
+ sCallTracker.incrementMethodCallCount();
+ }
+
+ /**
+ * Make sure ravenwoodRunnerInitializing() wasn't called.
+ */
+ @Test
+ public void testNotRavenized() {
+ sCallTracker.assertCalls(
+ "ravenwoodRunnerInitializing", 0
+ );
+ }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java
new file mode 100644
index 0000000..7e396c2
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.ravenwoodtest.bivalenttest.ravenizer;
+
+import android.util.Log;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Test to make sure {@link Suite} works with the ravenwood test runner.
+ */
+@RunWith(Suite.class)
[email protected]({
+ RavenwoodSuiteTest.Test1.class,
+ RavenwoodSuiteTest.Test2.class
+})
+public class RavenwoodSuiteTest {
+ public static final String TAG = "RavenwoodSuiteTest";
+
+ private static final CallTracker sCallTracker = new CallTracker();
+
+ @AfterClass
+ public static void afterClass() {
+ Log.i(TAG, "afterClass called");
+
+ sCallTracker.assertCallsOrDie(
+ "test1", 1,
+ "test2", 1
+ );
+ }
+
+ /**
+ * Workaround for the issue where tradefed won't think a class is a test class
+ * if it has a @RunWith but no @Test methods, even if it is a Suite.
+ */
+ @Test
+ public void testEmpty() {
+ }
+
+ public static class Test1 {
+ @Test
+ public void test1() {
+ sCallTracker.incrementMethodCallCount();
+ }
+ }
+
+ public static class Test2 {
+ @Test
+ public void test2() {
+ sCallTracker.incrementMethodCallCount();
+ }
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
index 1da93eb..68472c1 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
@@ -27,48 +27,69 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.os.RuntimeInit;
+import com.android.platform.test.ravenwood.runtimehelper.ClassLoadHook;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
import org.junit.runner.Description;
-import org.junit.runner.Runner;
import org.junit.runners.model.TestClass;
/**
* Provide hook points created by {@link RavenwoodAwareTestRunner}.
+ *
+ * States are associated with each {@link RavenwoodAwareTestRunner} are stored in
+ * {@link RavenwoodRunnerState}, rather than as members of {@link RavenwoodAwareTestRunner}.
+ * See its javadoc for the reasons.
+ *
+ * All methods in this class must be called from the test main thread.
*/
public class RavenwoodAwareTestRunnerHook {
- private static final String TAG = "RavenwoodAwareTestRunnerHook";
+ private static final String TAG = RavenwoodAwareTestRunner.TAG;
private RavenwoodAwareTestRunnerHook() {
}
- private static RavenwoodTestStats sStats; // lazy initialization.
- private static Description sCurrentClassDescription;
-
- private static RavenwoodTestStats getStats() {
- if (sStats == null) {
- // We don't want to throw in the static initializer, because tradefed may not report
- // it properly, so we initialize it here.
- sStats = new RavenwoodTestStats();
- }
- return sStats;
- }
-
/**
* Called when a runner starts, before the inner runner gets a chance to run.
*/
- public static void onRunnerInitializing(Runner runner, TestClass testClass) {
- // This log call also ensures the framework JNI is loaded.
- Log.i(TAG, "onRunnerInitializing: testClass=" + testClass + " runner=" + runner);
-
+ public static void onRunnerInitializing(RavenwoodAwareTestRunner runner, TestClass testClass) {
// TODO: Move the initialization code to a better place.
+ initOnce();
+
+ // This log call also ensures the framework JNI is loaded.
+ Log.i(TAG, "onRunnerInitializing: testClass=" + testClass.getJavaClass()
+ + " runner=" + runner);
+
+ // This is needed to make AndroidJUnit4ClassRunner happy.
+ InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+ }
+
+ private static boolean sInitialized = false;
+
+ private static void initOnce() {
+ if (sInitialized) {
+ return;
+ }
+ sInitialized = true;
+
+ // We haven't initialized liblog yet, so directly write to System.out here.
+ RavenwoodCommonUtils.log(TAG, "initOnce()");
+
+ // Make sure libandroid_runtime is loaded.
+ ClassLoadHook.onClassLoaded(Log.class);
+
+ // Redirect stdout/stdin to liblog.
+ RuntimeInit.redirectLogStreams();
+
// This will let AndroidJUnit4 use the original runner.
System.setProperty("android.junit.runner",
"androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner");
System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
-
- // This is needed to make AndroidJUnit4ClassRunner happy.
- InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+ // Do the basic set up for the android sysprops.
+ RavenwoodRuntimeEnvironmentController.setSystemProperties(
+ RavenwoodSystemProperties.DEFAULT_VALUES);
}
/**
@@ -76,7 +97,29 @@
*/
public static void onClassSkipped(Description description) {
Log.i(TAG, "onClassSkipped: description=" + description);
- getStats().onClassSkipped(description);
+ RavenwoodTestStats.getInstance().onClassSkipped(description);
+ }
+
+ /**
+ * Called before the inner runner starts.
+ */
+ public static void onBeforeInnerRunnerStart(
+ RavenwoodAwareTestRunner runner, Description description) throws Throwable {
+ Log.v(TAG, "onBeforeInnerRunnerStart: description=" + description);
+
+ // Prepare the environment before the inner runner starts.
+ RavenwoodRunnerState.forRunner(runner).enterTestClass(description);
+ }
+
+ /**
+ * Called after the inner runner finished.
+ */
+ public static void onAfterInnerRunnerFinished(
+ RavenwoodAwareTestRunner runner, Description description) throws Throwable {
+ Log.v(TAG, "onAfterInnerRunnerFinished: description=" + description);
+
+ RavenwoodTestStats.getInstance().onClassFinished(description);
+ RavenwoodRunnerState.forRunner(runner).exitTestClass();
}
/**
@@ -85,20 +128,23 @@
* Return false if it should be skipped.
*/
public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
- Scope scope, Order order) {
- Log.i(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
+ Scope scope, Order order) throws Throwable {
+ Log.v(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
- if (scope == Scope.Class && order == Order.First) {
- // Keep track of the current class.
- sCurrentClassDescription = description;
+ if (scope == Scope.Instance && order == Order.Outer) {
+ // Start of a test method.
+ RavenwoodRunnerState.forRunner(runner).enterTestMethod(description);
}
+ final var classDescription = RavenwoodRunnerState.forRunner(runner).getClassDescription();
+
// Class-level annotations are checked by the runner already, so we only check
// method-level annotations here.
- if (scope == Scope.Instance && order == Order.First) {
+ if (scope == Scope.Instance && order == Order.Outer) {
if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(
description, true)) {
- getStats().onTestFinished(sCurrentClassDescription, description, Result.Skipped);
+ RavenwoodTestStats.getInstance().onTestFinished(
+ classDescription, description, Result.Skipped);
return false;
}
}
@@ -112,19 +158,20 @@
*/
public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description,
Scope scope, Order order, Throwable th) {
- Log.i(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
+ Log.v(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
- if (scope == Scope.Instance && order == Order.First) {
- getStats().onTestFinished(sCurrentClassDescription, description,
+ final var classDescription = RavenwoodRunnerState.forRunner(runner).getClassDescription();
+
+ if (scope == Scope.Instance && order == Order.Outer) {
+ // End of a test method.
+ RavenwoodRunnerState.forRunner(runner).exitTestMethod();
+ RavenwoodTestStats.getInstance().onTestFinished(classDescription, description,
th == null ? Result.Passed : Result.Failed);
-
- } else if (scope == Scope.Class && order == Order.Last) {
- getStats().onClassFinished(sCurrentClassDescription);
}
// If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
if (RavenwoodRule.private$ravenwood().isRunningDisabledTests()
- && scope == Scope.Instance && order == Order.First) {
+ && scope == Scope.Instance && order == Order.Outer) {
boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood(
description, false);
@@ -159,4 +206,25 @@
public static boolean shouldRunClassOnRavenwood(Class<?> clazz) {
return RavenwoodEnablementChecker.shouldRunClassOnRavenwood(clazz, true);
}
-}
+
+ /**
+ * Called by RavenwoodRule.
+ */
+ public static void onRavenwoodRuleEnter(RavenwoodAwareTestRunner runner,
+ Description description, RavenwoodRule rule) throws Throwable {
+ Log.v(TAG, "onRavenwoodRuleEnter: description=" + description);
+
+ RavenwoodRunnerState.forRunner(runner).enterRavenwoodRule(rule);
+ }
+
+
+ /**
+ * Called by RavenwoodRule.
+ */
+ public static void onRavenwoodRuleExit(RavenwoodAwareTestRunner runner,
+ Description description, RavenwoodRule rule) throws Throwable {
+ Log.v(TAG, "onRavenwoodRuleExit: description=" + description);
+
+ RavenwoodRunnerState.forRunner(runner).exitRavenwoodRule(rule);
+ }
+}
\ No newline at end of file
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
new file mode 100644
index 0000000..d73afd4
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -0,0 +1,252 @@
+/*
+ * 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 android.platform.test.ravenwood;
+
+import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicMember;
+
+import static org.junit.Assert.fail;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.ravenwood.common.RavenwoodRuntimeException;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.WeakHashMap;
+
+/**
+ * Used to store various states associated with the current test runner.
+ *
+ * This class could be added to {@link RavenwoodAwareTestRunner} as a field, but we don't
+ * want to put it in junit-src/ (for one, that'll cause all the downstream dependencies to be
+ * rebuilt when this file is updated), so we manage it separately using a Map from each
+ * {@link RavenwoodAwareTestRunner} instance to a {@link RavenwoodRunnerState}.
+ *
+ * All members must be called from the runner's main thread.
+ */
+public final class RavenwoodRunnerState {
+ private static final String TAG = "RavenwoodRunnerState";
+
+ @GuardedBy("sStates")
+ private static final WeakHashMap<RavenwoodAwareTestRunner, RavenwoodRunnerState> sStates =
+ new WeakHashMap<>();
+
+ /**
+ * Get the instance for a given runner.
+ */
+ public static RavenwoodRunnerState forRunner(@NonNull RavenwoodAwareTestRunner runner) {
+ synchronized (sStates) {
+ var ret = sStates.get(runner);
+ if (ret == null) {
+ ret = new RavenwoodRunnerState(runner);
+ sStates.put(runner, ret);
+ }
+ return ret;
+ }
+ }
+
+ private final RavenwoodAwareTestRunner mRunner;
+
+ private RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
+ mRunner = runner;
+ }
+
+ private Description mClassDescription;
+ private Description mMethodDescription;
+
+ private RavenwoodConfig mCurrentConfig;
+ private RavenwoodRule mCurrentRule;
+
+ public Description getClassDescription() {
+ return mClassDescription;
+ }
+
+ public void enterTestClass(Description classDescription) throws IOException {
+ mClassDescription = classDescription;
+
+ mCurrentConfig = extractConfiguration(mRunner.getTestClass().getJavaClass());
+
+ if (mCurrentConfig != null) {
+ RavenwoodRuntimeEnvironmentController.init(mCurrentConfig);
+ }
+ }
+
+ public void exitTestClass() {
+ if (mCurrentConfig != null) {
+ try {
+ RavenwoodRuntimeEnvironmentController.reset();
+ } finally {
+ mClassDescription = null;
+ }
+ }
+ }
+
+ public void enterTestMethod(Description description) {
+ mMethodDescription = description;
+ }
+
+ public void exitTestMethod() {
+ mMethodDescription = null;
+ }
+
+ public void enterRavenwoodRule(RavenwoodRule rule) throws IOException {
+ if (mCurrentConfig != null) {
+ fail("RavenwoodConfiguration and RavenwoodRule cannot be used in the same class."
+ + " Suggest migrating to RavenwoodConfiguration.");
+ }
+ if (mCurrentRule != null) {
+ fail("Multiple nesting RavenwoodRule's are detected in the same class,"
+ + " which is not supported.");
+ }
+ mCurrentRule = rule;
+ RavenwoodRuntimeEnvironmentController.init(rule.getConfiguration());
+ }
+
+ public void exitRavenwoodRule(RavenwoodRule rule) {
+ if (mCurrentRule != rule) {
+ return; // This happens if the rule did _not_ take effect somehow.
+ }
+
+ try {
+ RavenwoodRuntimeEnvironmentController.reset();
+ } finally {
+ mCurrentRule = null;
+ }
+ }
+
+ /**
+ * @return a configuration from a test class, if any.
+ */
+ @Nullable
+ private static RavenwoodConfig extractConfiguration(Class<?> testClass) {
+ final boolean hasRavenwoodRule = hasRavenwoodRule(testClass);
+
+ var field = findConfigurationField(testClass);
+ if (field == null) {
+ return null;
+ }
+ if (hasRavenwoodRule) {
+ fail("RavenwoodConfiguration and RavenwoodRule cannot be used in the same class."
+ + " Suggest migrating to RavenwoodConfiguration.");
+ }
+
+ try {
+ return (RavenwoodConfig) field.get(null);
+ } catch (IllegalAccessException e) {
+ throw new RavenwoodRuntimeException("Failed to fetch from the configuration field", e);
+ }
+ }
+
+ /**
+ * @return true if the current target class (or its super classes) has any @Rule / @ClassRule
+ * fields of type RavenwoodRule.
+ *
+ * Note, this check won't detect cases where a Rule is of type
+ * {@link TestRule} and still be a {@link RavenwoodRule}. But that'll be detected at runtime
+ * as a failure, in {@link #enterRavenwoodRule}.
+ */
+ private static boolean hasRavenwoodRule(Class<?> testClass) {
+ for (var field : testClass.getDeclaredFields()) {
+ if (!field.isAnnotationPresent(Rule.class)
+ && field.isAnnotationPresent(ClassRule.class)) {
+ continue;
+ }
+ if (field.getType().equals(RavenwoodRule.class)) {
+ return true;
+ }
+ }
+ // JUnit supports rules as methods, so we need to check them too.
+ for (var method : testClass.getDeclaredMethods()) {
+ if (!method.isAnnotationPresent(Rule.class)
+ && method.isAnnotationPresent(ClassRule.class)) {
+ continue;
+ }
+ if (method.getReturnType().equals(RavenwoodRule.class)) {
+ return true;
+ }
+ }
+ // Look into the super class.
+ if (!testClass.getSuperclass().equals(Object.class)) {
+ return hasRavenwoodRule(testClass.getSuperclass());
+ }
+ return false;
+ }
+
+ /**
+ * Find and return a field with @RavenwoodConfiguration.Config, which must be of type
+ * RavenwoodConfiguration.
+ */
+ @Nullable
+ private static Field findConfigurationField(Class<?> testClass) {
+ Field foundField = null;
+
+ for (var field : testClass.getDeclaredFields()) {
+ final var hasAnot = field.isAnnotationPresent(RavenwoodConfig.Config.class);
+ final var isType = field.getType().equals(RavenwoodConfig.class);
+
+ if (hasAnot) {
+ if (isType) {
+ // Good, use this field.
+ if (foundField != null) {
+ fail(String.format(
+ "Class %s has multiple fields with %s",
+ testClass.getCanonicalName(),
+ "@RavenwoodConfiguration.Config"));
+ }
+ // Make sure it's static public
+ ensureIsPublicMember(field, true);
+
+ foundField = field;
+ } else {
+ fail(String.format(
+ "Field %s.%s has %s but type is not %s",
+ testClass.getCanonicalName(),
+ field.getName(),
+ "@RavenwoodConfiguration.Config",
+ "RavenwoodConfiguration"));
+ return null; // unreachable
+ }
+ } else {
+ if (isType) {
+ fail(String.format(
+ "Field %s.%s does not have %s but type is %s",
+ testClass.getCanonicalName(),
+ field.getName(),
+ "@RavenwoodConfiguration.Config",
+ "RavenwoodConfiguration"));
+ return null; // unreachable
+ } else {
+ // Unrelated field, ignore.
+ continue;
+ }
+ }
+ }
+ if (foundField != null) {
+ return foundField;
+ }
+ if (!testClass.getSuperclass().equals(Object.class)) {
+ return findConfigurationField(testClass.getSuperclass());
+ }
+ return null;
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
similarity index 66%
rename from ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
rename to ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index a2088fd..03c9001 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -18,6 +18,7 @@
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -26,6 +27,7 @@
import android.app.Instrumentation;
import android.app.ResourcesManager;
import android.content.res.Resources;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.HandlerThread;
@@ -36,7 +38,8 @@
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.internal.os.RuntimeInit;
+import com.android.ravenwood.common.RavenwoodRuntimeException;
+import com.android.ravenwood.common.SneakyThrow;
import com.android.server.LocalServices;
import org.junit.runner.Description;
@@ -53,14 +56,25 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
-public class RavenwoodRuleImpl {
+/**
+ * Responsible for initializing and de-initializing the environment, according to a
+ * {@link RavenwoodConfig}.
+ */
+public class RavenwoodRuntimeEnvironmentController {
+ private static final String TAG = "RavenwoodRuntimeEnvironmentController";
+
+ private RavenwoodRuntimeEnvironmentController() {
+ }
+
private static final String MAIN_THREAD_NAME = "RavenwoodMain";
/**
* When enabled, attempt to dump all thread stacks just before we hit the
* overall Tradefed timeout, to aid in debugging deadlocks.
*/
- private static final boolean ENABLE_TIMEOUT_STACKS = false;
+ private static final boolean ENABLE_TIMEOUT_STACKS =
+ "1".equals(System.getenv("RAVENWOOD_ENABLE_TIMEOUT_STACKS"));
+
private static final int TIMEOUT_MILLIS = 9_000;
private static final ScheduledExecutorService sTimeoutExecutor =
@@ -68,10 +82,13 @@
private static ScheduledFuture<?> sPendingTimeout;
+ private static long sOriginalIdentityToken = -1;
+
/**
* When enabled, attempt to detect uncaught exceptions from background threads.
*/
- private static final boolean ENABLE_UNCAUGHT_EXCEPTION_DETECTION = false;
+ private static final boolean ENABLE_UNCAUGHT_EXCEPTION_DETECTION =
+ "1".equals(System.getenv("RAVENWOOD_ENABLE_UNCAUGHT_EXCEPTION_DETECTION"));
/**
* When set, an unhandled exception was discovered (typically on a background thread), and we
@@ -86,25 +103,59 @@
sPendingUncaughtException.compareAndSet(null, throwable);
};
- public static void init(RavenwoodRule rule) throws IOException {
+ // TODO: expose packCallingIdentity function in libbinder and use it directly
+ // See: packCallingIdentity in frameworks/native/libs/binder/IPCThreadState.cpp
+ private static long packBinderIdentityToken(
+ boolean hasExplicitIdentity, int callingUid, int callingPid) {
+ long res = ((long) callingUid << 32) | callingPid;
+ if (hasExplicitIdentity) {
+ res |= (0x1 << 30);
+ } else {
+ res &= ~(0x1 << 30);
+ }
+ return res;
+ }
+
+ private static RavenwoodConfig sConfig;
+
+ /**
+ * Initialize the environment.
+ */
+ public static void init(RavenwoodConfig config) throws IOException {
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.i(TAG, "init() called here", new RuntimeException("STACKTRACE"));
+ }
+ try {
+ initInner(config);
+ } catch (Exception th) {
+ Log.e(TAG, "init() failed", th);
+ reset();
+ SneakyThrow.sneakyThrow(th);
+ }
+ }
+
+ private static void initInner(RavenwoodConfig config) throws IOException {
+ if (sConfig != null) {
+ throw new RavenwoodRuntimeException("Internal error: init() called without reset()");
+ }
+ sConfig = config;
if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
maybeThrowPendingUncaughtException(false);
Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
}
- RuntimeInit.redirectLogStreams();
-
- android.os.Process.init$ravenwood(rule.mUid, rule.mPid);
- android.os.Binder.init$ravenwood();
- setSystemProperties(rule.mSystemProperties);
+ android.os.Process.init$ravenwood(config.mUid, config.mPid);
+ sOriginalIdentityToken = Binder.clearCallingIdentity();
+ Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
+ setSystemProperties(config.mSystemProperties);
ServiceManager.init$ravenwood();
LocalServices.removeAllServicesForTest();
- ActivityManager.init$ravenwood(rule.mCurrentUser);
+ ActivityManager.init$ravenwood(config.mCurrentUser);
final HandlerThread main;
- if (rule.mProvideMainThread) {
+ if (config.mProvideMainThread) {
main = new HandlerThread(MAIN_THREAD_NAME);
main.start();
Looper.setMainLooperForTest(main.getLooper());
@@ -128,21 +179,22 @@
emptyPaths, emptyPaths, emptyPaths,
emptyPaths, null, null,
new DisplayAdjustments().getCompatibilityInfo(),
- RavenwoodRuleImpl.class.getClassLoader(), null);
+ RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null);
assertNotNull(ret);
return ret;
};
- rule.mContext = new RavenwoodContext(rule.mPackageName, main, resourcesSupplier);
- rule.mInstrumentation = new Instrumentation();
- rule.mInstrumentation.basicInit(rule.mContext);
- InstrumentationRegistry.registerInstance(rule.mInstrumentation, Bundle.EMPTY);
+ config.mContext = new RavenwoodContext(config.mPackageName, main, resourcesSupplier);
+ config.mInstrumentation = new Instrumentation();
+ config.mInstrumentation.basicInit(config.mContext);
+ InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY);
- RavenwoodSystemServer.init(rule);
+ RavenwoodSystemServer.init(config);
if (ENABLE_TIMEOUT_STACKS) {
- sPendingTimeout = sTimeoutExecutor.schedule(RavenwoodRuleImpl::dumpStacks,
+ sPendingTimeout = sTimeoutExecutor.schedule(
+ RavenwoodRuntimeEnvironmentController::dumpStacks,
TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}
@@ -151,21 +203,33 @@
Objects.requireNonNull(Build.VERSION.SDK);
}
- public static void reset(RavenwoodRule rule) {
+ /**
+ * De-initialize.
+ */
+ public static void reset() {
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.i(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
+ }
+ if (sConfig == null) {
+ throw new RavenwoodRuntimeException("Internal error: reset() already called");
+ }
+ var config = sConfig;
+ sConfig = null;
+
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout.cancel(false);
}
- RavenwoodSystemServer.reset(rule);
+ RavenwoodSystemServer.reset(config);
InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
- rule.mInstrumentation = null;
- if (rule.mContext != null) {
- ((RavenwoodContext) rule.mContext).cleanUp();
+ config.mInstrumentation = null;
+ if (config.mContext != null) {
+ ((RavenwoodContext) config.mContext).cleanUp();
}
- rule.mContext = null;
+ config.mContext = null;
- if (rule.mProvideMainThread) {
+ if (config.mProvideMainThread) {
Looper.getMainLooper().quit();
Looper.clearMainLooperForTest();
}
@@ -176,7 +240,7 @@
ServiceManager.reset$ravenwood();
setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
- android.os.Binder.reset$ravenwood();
+ Binder.restoreCallingIdentity(sOriginalIdentityToken);
android.os.Process.reset$ravenwood();
ResourcesManager.setInstance(null); // Better structure needed.
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index cd6b61d..f3a93c1 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -61,19 +61,19 @@
private static TimingsTraceAndSlog sTimings;
private static SystemServiceManager sServiceManager;
- public static void init(RavenwoodRule rule) {
+ public static void init(RavenwoodConfig config) {
// Avoid overhead if no services required
- if (rule.mServicesRequired.isEmpty()) return;
+ if (config.mServicesRequired.isEmpty()) return;
sStartedServices = new ArraySet<>();
sTimings = new TimingsTraceAndSlog();
- sServiceManager = new SystemServiceManager(rule.mContext);
+ sServiceManager = new SystemServiceManager(config.mContext);
sServiceManager.setStartInfo(false,
SystemClock.elapsedRealtime(),
SystemClock.uptimeMillis());
LocalServices.addService(SystemServiceManager.class, sServiceManager);
- startServices(rule.mServicesRequired);
+ startServices(config.mServicesRequired);
sServiceManager.sealStartedServices();
// TODO: expand to include additional boot phases when relevant
@@ -81,7 +81,7 @@
sServiceManager.startBootPhase(sTimings, SystemService.PHASE_BOOT_COMPLETED);
}
- public static void reset(RavenwoodRule rule) {
+ public static void reset(RavenwoodConfig config) {
// TODO: consider introducing shutdown boot phases
LocalServices.removeServiceForTest(SystemServiceManager.class);
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index 631f68f..428eb57 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -31,7 +31,7 @@
import java.util.Map;
/**
- * Creats a "stats" CSV file containing the test results.
+ * Collect test result stats and write them into a CSV file containing the test results.
*
* The output file is created as `/tmp/Ravenwood-stats_[TEST-MODULE=NAME]_[TIMESTAMP].csv`.
* A symlink to the latest result will be created as
@@ -41,6 +41,21 @@
private static final String TAG = "RavenwoodTestStats";
private static final String HEADER = "Module,Class,ClassDesc,Passed,Failed,Skipped";
+ private static RavenwoodTestStats sInstance;
+
+ /**
+ * @return a singleton instance.
+ */
+ public static RavenwoodTestStats getInstance() {
+ if (sInstance == null) {
+ sInstance = new RavenwoodTestStats();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Represents a test result.
+ */
public enum Result {
Passed,
Failed,
@@ -113,21 +128,34 @@
});
}
+ /**
+ * Call it when a test class is skipped.
+ */
public void onClassSkipped(Description classDescription) {
addResult(classDescription, Description.EMPTY, Result.Skipped);
onClassFinished(classDescription);
}
+ /**
+ * Call it when a test method is finished.
+ */
public void onTestFinished(Description classDescription, Description testDescription,
Result result) {
addResult(classDescription, testDescription, result);
}
+ /**
+ * Call it when a test class is finished.
+ */
public void onClassFinished(Description classDescription) {
int passed = 0;
int skipped = 0;
int failed = 0;
- for (var e : mStats.get(classDescription).values()) {
+ var stats = mStats.get(classDescription);
+ if (stats == null) {
+ return;
+ }
+ for (var e : stats.values()) {
switch (e) {
case Passed: passed++; break;
case Skipped: skipped++; break;
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java b/ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java
similarity index 62%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
rename to ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java
index 76bbcad..a84f16f 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
+++ b/ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -13,16 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.hoststubgen.test.tinyframework;
+
+package android.platform.test.annotations;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * We don't want to use any android classes in this module, so we create our own copy of it here.
+ * Disable the ravenizer preprocessor for a class. This should be only used for testing
+ * ravenizer itself, or to workaround issues with the preprocessor. A test class probably won't run
+ * properly if it's not preprocessed.
+ *
+ * @hide
*/
+@Inherited
+@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface LargeTest {}
+public @interface NoRavenizer {
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 2b55ac5..bc944d7 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -15,18 +15,23 @@
*/
package android.platform.test.ravenwood;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod;
import static com.android.ravenwood.common.RavenwoodCommonUtils.isOnRavenwood;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
-import com.android.ravenwood.common.RavenwoodCommonUtils;
-import com.android.ravenwood.common.SneakyThrow;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
import org.junit.Assume;
+import org.junit.AssumptionViolatedException;
+import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
+import org.junit.runner.Result;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.Filterable;
@@ -36,8 +41,13 @@
import org.junit.runner.manipulation.Orderer;
import org.junit.runner.manipulation.Sortable;
import org.junit.runner.manipulation.Sorter;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
+import org.junit.runner.notification.StoppedByUserException;
import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.MultipleFailureException;
+import org.junit.runners.model.RunnerBuilder;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
@@ -47,19 +57,20 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Stack;
+import java.util.function.BiConsumer;
/**
* A test runner used for Ravenwood.
*
- * TODO: Handle ENABLE_PROBE_IGNORED
- *
* It will delegate to another runner specified with {@link InnerRunner}
* (default = {@link BlockJUnit4ClassRunner}) with the following features.
* - Add a {@link RavenwoodAwareTestRunnerHook#onRunnerInitializing} hook, which is called before
* the inner runner gets a chance to run. This can be used to initialize stuff used by the
* inner runner.
* - Add hook points, which are handed by RavenwoodAwareTestRunnerHook, with help from
- * the four test rules such as {@link #sImplicitClassMinRule}, which are also injected by
+ * the four test rules such as {@link #sImplicitClassOuterRule}, which are also injected by
* the ravenizer tool.
*
* We use this runner to:
@@ -71,8 +82,8 @@
* it will basically just delegate to the inner wrapper, and won't do anything special.
* (no hooks, etc.)
*/
-public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orderable {
- private static final String TAG = "RavenwoodAwareTestRunner";
+public final class RavenwoodAwareTestRunner extends Runner implements Filterable, Orderable {
+ public static final String TAG = "Ravenwood";
@Inherited
@Target({TYPE})
@@ -93,40 +104,61 @@
/** Scope of a hook. */
public enum Scope {
- Runner,
Class,
Instance,
}
/** Order of a hook. */
public enum Order {
- First,
- Last,
+ Outer,
+ Inner,
}
// The following four rule instances will be injected to tests by the Ravenizer tool.
+ private static class RavenwoodClassOuterRule implements TestRule {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return getCurrentRunner().wrapWithHooks(base, description, Scope.Class, Order.Outer);
+ }
+ }
- public static final TestRule sImplicitClassMinRule = (base, description) ->
- getCurrentRunner().updateStatement(base, description, Scope.Class, Order.First);
+ private static class RavenwoodClassInnerRule implements TestRule {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return getCurrentRunner().wrapWithHooks(base, description, Scope.Class, Order.Inner);
+ }
+ }
- public static final TestRule sImplicitClassMaxRule = (base, description) ->
- getCurrentRunner().updateStatement(base, description, Scope.Class, Order.Last);
+ private static class RavenwoodInstanceOuterRule implements TestRule {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return getCurrentRunner().wrapWithHooks(
+ base, description, Scope.Instance, Order.Outer);
+ }
+ }
- public static final TestRule sImplicitInstMinRule = (base, description) ->
- getCurrentRunner().updateStatement(base, description, Scope.Instance, Order.First);
+ private static class RavenwoodInstanceInnerRule implements TestRule {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return getCurrentRunner().wrapWithHooks(
+ base, description, Scope.Instance, Order.Inner);
+ }
+ }
- public static final TestRule sImplicitInstMaxRule = (base, description) ->
- getCurrentRunner().updateStatement(base, description, Scope.Instance, Order.Last);
+ public static final TestRule sImplicitClassOuterRule = new RavenwoodClassOuterRule();
+ public static final TestRule sImplicitClassInnerRule = new RavenwoodClassInnerRule();
+ public static final TestRule sImplicitInstOuterRule = new RavenwoodInstanceOuterRule();
+ public static final TestRule sImplicitInstInnerRule = new RavenwoodInstanceOuterRule();
- public static final String IMPLICIT_CLASS_MIN_RULE_NAME = "sImplicitClassMinRule";
- public static final String IMPLICIT_CLASS_MAX_RULE_NAME = "sImplicitClassMaxRule";
- public static final String IMPLICIT_INST_MIN_RULE_NAME = "sImplicitInstMinRule";
- public static final String IMPLICIT_INST_MAX_RULE_NAME = "sImplicitInstMaxRule";
+ public static final String IMPLICIT_CLASS_OUTER_RULE_NAME = "sImplicitClassOuterRule";
+ public static final String IMPLICIT_CLASS_INNER_RULE_NAME = "sImplicitClassInnerRule";
+ public static final String IMPLICIT_INST_OUTER_RULE_NAME = "sImplicitInstOuterRule";
+ public static final String IMPLICIT_INST_INNER_RULE_NAME = "sImplicitInstInnerRule";
/** Keeps track of the runner on the current thread. */
private static final ThreadLocal<RavenwoodAwareTestRunner> sCurrentRunner = new ThreadLocal<>();
- private static RavenwoodAwareTestRunner getCurrentRunner() {
+ static RavenwoodAwareTestRunner getCurrentRunner() {
var runner = sCurrentRunner.get();
if (runner == null) {
throw new RuntimeException("Current test runner not set!");
@@ -134,60 +166,88 @@
return runner;
}
- private final TestClass mTestClsas;
- private final Runner mRealRunner;
+ private TestClass mTestClass = null;
+ private Runner mRealRunner = null;
+ private Description mDescription = null;
+ private Throwable mExceptionInConstructor = null;
- /** Simple logging method. */
- private void log(String message) {
- RavenwoodCommonUtils.log(TAG, "[" + getTestClass() + " @" + this + "] " + message);
- }
-
- private Error logAndFail(String message, Throwable innerException) {
- log(message);
- log(" Exception=" + innerException);
- throw new AssertionError(message, innerException);
+ private Error logAndFail(String message, Throwable exception) {
+ Log.e(TAG, message, exception);
+ throw new AssertionError(message, exception);
}
public TestClass getTestClass() {
- return mTestClsas;
+ return mTestClass;
}
/**
* Constructor.
*/
public RavenwoodAwareTestRunner(Class<?> testClass) {
- mTestClsas = new TestClass(testClass);
-
- /*
- * If the class has @DisabledOnRavenwood, then we'll delegate to ClassSkippingTestRunner,
- * which simply skips it.
- */
- if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood(
- mTestClsas.getJavaClass())) {
- mRealRunner = new ClassSkippingTestRunner(mTestClsas);
- return;
- }
-
- // Find the real runner.
- final Class<? extends Runner> realRunner;
- final InnerRunner innerRunnerAnnotation = mTestClsas.getAnnotation(InnerRunner.class);
- if (innerRunnerAnnotation != null) {
- realRunner = innerRunnerAnnotation.value();
- } else {
- // Default runner.
- realRunner = BlockJUnit4ClassRunner.class;
- }
-
- onRunnerInitializing();
-
try {
- log("Initializing the inner runner: " + realRunner);
+ mTestClass = new TestClass(testClass);
- mRealRunner = realRunner.getConstructor(Class.class).newInstance(testClass);
+ Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
- } catch (InstantiationException | IllegalAccessException
- | InvocationTargetException | NoSuchMethodException e) {
- throw logAndFail("Failed to instantiate " + realRunner, e);
+ onRunnerInitializing();
+
+ /*
+ * If the class has @DisabledOnRavenwood, then we'll delegate to
+ * ClassSkippingTestRunner, which simply skips it.
+ */
+ if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood(
+ mTestClass.getJavaClass())) {
+ mRealRunner = new ClassSkippingTestRunner(mTestClass);
+ mDescription = mRealRunner.getDescription();
+ return;
+ }
+
+ // Find the real runner.
+ final Class<? extends Runner> realRunnerClass;
+ final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class);
+ if (innerRunnerAnnotation != null) {
+ realRunnerClass = innerRunnerAnnotation.value();
+ } else {
+ // Default runner.
+ realRunnerClass = BlockJUnit4ClassRunner.class;
+ }
+
+ try {
+ Log.i(TAG, "Initializing the inner runner: " + realRunnerClass);
+
+ mRealRunner = instantiateRealRunner(realRunnerClass, testClass);
+ mDescription = mRealRunner.getDescription();
+
+ } catch (InstantiationException | IllegalAccessException
+ | InvocationTargetException | NoSuchMethodException e) {
+ throw logAndFail("Failed to instantiate " + realRunnerClass, e);
+ }
+ } catch (Throwable th) {
+ // If we throw in the constructor, Tradefed may not report it and just ignore the class,
+ // so record it and throw it when the test actually started.
+ Log.e(TAG, "Fatal: Exception detected in constructor", th);
+ mExceptionInConstructor = new RuntimeException("Exception detected in constructor",
+ th);
+ mDescription = Description.createTestDescription(testClass, "Constructor");
+
+ // This is for testing if tradefed is fixed.
+ if ("1".equals(System.getenv("RAVENWOOD_THROW_EXCEPTION_IN_TEST_RUNNER"))) {
+ throw th;
+ }
+ }
+ }
+
+ private static Runner instantiateRealRunner(
+ Class<? extends Runner> realRunnerClass,
+ Class<?> testClass)
+ throws NoSuchMethodException, InvocationTargetException, InstantiationException,
+ IllegalAccessException {
+ try {
+ return realRunnerClass.getConstructor(Class.class).newInstance(testClass);
+ } catch (NoSuchMethodException e) {
+ var runnerBuilder = new AllDefaultPossibilitiesBuilder();
+ return realRunnerClass.getConstructor(Class.class,
+ RunnerBuilder.class).newInstance(testClass, runnerBuilder);
}
}
@@ -199,10 +259,9 @@
if (!isOnRavenwood()) {
return;
}
+ // DO NOT USE android.util.Log before calling onRunnerInitializing().
- log("onRunnerInitializing");
-
- RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClsas);
+ RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass);
// Hook point to allow more customization.
runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
@@ -213,7 +272,7 @@
if (!isOnRavenwood()) {
return;
}
- log("runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+ Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
for (var method : getTestClass().getAnnotatedMethods(annotationClass)) {
ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
@@ -230,26 +289,65 @@
@Override
public Description getDescription() {
- return mRealRunner.getDescription();
+ return mDescription;
}
@Override
- public void run(RunNotifier notifier) {
+ public void run(RunNotifier realNotifier) {
+ final RavenwoodRunNotifier notifier = new RavenwoodRunNotifier(realNotifier);
+
if (mRealRunner instanceof ClassSkippingTestRunner) {
mRealRunner.run(notifier);
RavenwoodAwareTestRunnerHook.onClassSkipped(getDescription());
return;
}
+ Log.v(TAG, "Starting " + mTestClass.getJavaClass().getCanonicalName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ dumpDescription(getDescription());
+ }
+
+ if (maybeReportExceptionFromConstructor(notifier)) {
+ return;
+ }
+
sCurrentRunner.set(this);
try {
- runWithHooks(getDescription(), Scope.Runner, Order.First,
- () -> mRealRunner.run(notifier));
+ try {
+ RavenwoodAwareTestRunnerHook.onBeforeInnerRunnerStart(
+ this, getDescription());
+ } catch (Throwable th) {
+ notifier.reportBeforeTestFailure(getDescription(), th);
+ return;
+ }
+
+ // Delegate to the inner runner.
+ mRealRunner.run(notifier);
} finally {
sCurrentRunner.remove();
+
+ try {
+ RavenwoodAwareTestRunnerHook.onAfterInnerRunnerFinished(
+ this, getDescription());
+ } catch (Throwable th) {
+ notifier.reportAfterTestFailure(th);
+ return;
+ }
}
}
+ /** Throw the exception detected in the constructor, if any. */
+ private boolean maybeReportExceptionFromConstructor(RunNotifier notifier) {
+ if (mExceptionInConstructor == null) {
+ return false;
+ }
+ notifier.fireTestStarted(mDescription);
+ notifier.fireTestFailure(new Failure(mDescription, mExceptionInConstructor));
+ notifier.fireTestFinished(mDescription);
+
+ return true;
+ }
+
@Override
public void filter(Filter filter) throws NoTestsRemainException {
if (mRealRunner instanceof Filterable r) {
@@ -271,7 +369,7 @@
}
}
- private Statement updateStatement(Statement base, Description description, Scope scope,
+ private Statement wrapWithHooks(Statement base, Description description, Scope scope,
Order order) {
if (!isOnRavenwood()) {
return base;
@@ -284,7 +382,8 @@
};
}
- private void runWithHooks(Description description, Scope scope, Order order, Runnable r) {
+ private void runWithHooks(Description description, Scope scope, Order order, Runnable r)
+ throws Throwable {
runWithHooks(description, scope, order, new Statement() {
@Override
public void evaluate() throws Throwable {
@@ -293,7 +392,8 @@
});
}
- private void runWithHooks(Description description, Scope scope, Order order, Statement s) {
+ private void runWithHooks(Description description, Scope scope, Order order, Statement s)
+ throws Throwable {
if (isOnRavenwood()) {
Assume.assumeTrue(
RavenwoodAwareTestRunnerHook.onBefore(this, description, scope, order));
@@ -310,7 +410,7 @@
this, description, scope, order, t);
}
if (shouldThrow) {
- SneakyThrow.sneakyThrow(t);
+ throw t;
}
}
}
@@ -356,4 +456,254 @@
}
}
}
+
+ private void dumpDescription(Description desc) {
+ dumpDescription(desc, "[TestDescription]=", " ");
+ }
+
+ private void dumpDescription(Description desc, String header, String indent) {
+ Log.v(TAG, indent + header + desc);
+
+ var children = desc.getChildren();
+ var childrenIndent = " " + indent;
+ for (int i = 0; i < children.size(); i++) {
+ dumpDescription(children.get(i), "#" + i + ": ", childrenIndent);
+ }
+ }
+
+ /**
+ * A run notifier that wraps another notifier and provides the following features:
+ * - Handle a failure that happened before testStarted and testEnded (typically that means
+ * it's from @BeforeClass or @AfterClass, or a @ClassRule) and deliver it as if
+ * individual tests in the class reported it. This is for b/364395552.
+ *
+ * - Logging.
+ */
+ private class RavenwoodRunNotifier extends RunNotifier {
+ private final RunNotifier mRealNotifier;
+
+ private final Stack<Description> mSuiteStack = new Stack<>();
+ private Description mCurrentSuite = null;
+ private final ArrayList<Throwable> mOutOfTestFailures = new ArrayList<>();
+
+ private boolean mBeforeTest = true;
+ private boolean mAfterTest = false;
+
+ private RavenwoodRunNotifier(RunNotifier realNotifier) {
+ mRealNotifier = realNotifier;
+ }
+
+ private boolean isInTest() {
+ return !mBeforeTest && !mAfterTest;
+ }
+
+ @Override
+ public void addListener(RunListener listener) {
+ mRealNotifier.addListener(listener);
+ }
+
+ @Override
+ public void removeListener(RunListener listener) {
+ mRealNotifier.removeListener(listener);
+ }
+
+ @Override
+ public void addFirstListener(RunListener listener) {
+ mRealNotifier.addFirstListener(listener);
+ }
+
+ @Override
+ public void fireTestRunStarted(Description description) {
+ Log.i(TAG, "testRunStarted: " + description);
+ mRealNotifier.fireTestRunStarted(description);
+ }
+
+ @Override
+ public void fireTestRunFinished(Result result) {
+ Log.i(TAG, "testRunFinished: "
+ + result.getRunCount() + ","
+ + result.getFailureCount() + ","
+ + result.getAssumptionFailureCount() + ","
+ + result.getIgnoreCount());
+ mRealNotifier.fireTestRunFinished(result);
+ }
+
+ @Override
+ public void fireTestSuiteStarted(Description description) {
+ Log.i(TAG, "testSuiteStarted: " + description);
+ mRealNotifier.fireTestSuiteStarted(description);
+
+ mBeforeTest = true;
+ mAfterTest = false;
+
+ // Keep track of the current suite, needed if the outer test is a Suite,
+ // in which case its children are test classes. (not test methods)
+ mCurrentSuite = description;
+ mSuiteStack.push(description);
+
+ mOutOfTestFailures.clear();
+ }
+
+ @Override
+ public void fireTestSuiteFinished(Description description) {
+ Log.i(TAG, "testSuiteFinished: " + description);
+ mRealNotifier.fireTestSuiteFinished(description);
+
+ maybeHandleOutOfTestFailures();
+
+ mBeforeTest = true;
+ mAfterTest = false;
+
+ // Restore the upper suite.
+ mSuiteStack.pop();
+ mCurrentSuite = mSuiteStack.size() == 0 ? null : mSuiteStack.peek();
+ }
+
+ @Override
+ public void fireTestStarted(Description description) throws StoppedByUserException {
+ Log.i(TAG, "testStarted: " + description);
+ mRealNotifier.fireTestStarted(description);
+
+ mAfterTest = false;
+ mBeforeTest = false;
+ }
+
+ @Override
+ public void fireTestFailure(Failure failure) {
+ Log.i(TAG, "testFailure: " + failure);
+
+ if (isInTest()) {
+ mRealNotifier.fireTestFailure(failure);
+ } else {
+ mOutOfTestFailures.add(failure.getException());
+ }
+ }
+
+ @Override
+ public void fireTestAssumptionFailed(Failure failure) {
+ Log.i(TAG, "testAssumptionFailed: " + failure);
+
+ if (isInTest()) {
+ mRealNotifier.fireTestAssumptionFailed(failure);
+ } else {
+ mOutOfTestFailures.add(failure.getException());
+ }
+ }
+
+ @Override
+ public void fireTestIgnored(Description description) {
+ Log.i(TAG, "testIgnored: " + description);
+ mRealNotifier.fireTestIgnored(description);
+ }
+
+ @Override
+ public void fireTestFinished(Description description) {
+ Log.i(TAG, "testFinished: " + description);
+ mRealNotifier.fireTestFinished(description);
+
+ mAfterTest = true;
+ }
+
+ @Override
+ public void pleaseStop() {
+ Log.w(TAG, "pleaseStop:");
+ mRealNotifier.pleaseStop();
+ }
+
+ /**
+ * At the end of each Suite, we handle failures happened out of test methods.
+ * (typically in @BeforeClass or @AfterClasses)
+ *
+ * This is to work around b/364395552.
+ */
+ private boolean maybeHandleOutOfTestFailures() {
+ if (mOutOfTestFailures.size() == 0) {
+ return false;
+ }
+ Throwable th;
+ if (mOutOfTestFailures.size() == 1) {
+ th = mOutOfTestFailures.get(0);
+ } else {
+ th = new MultipleFailureException(mOutOfTestFailures);
+ }
+ if (mBeforeTest) {
+ reportBeforeTestFailure(mCurrentSuite, th);
+ return true;
+ }
+ if (mAfterTest) {
+ reportAfterTestFailure(th);
+ return true;
+ }
+ return false;
+ }
+
+ public void reportBeforeTestFailure(Description suiteDesc, Throwable th) {
+ // If a failure happens befere running any tests, we'll need to pretend
+ // as if each test in the suite reported the failure, to work around b/364395552.
+ for (var child : suiteDesc.getChildren()) {
+ if (child.isSuite()) {
+ // If the chiil is still a "parent" -- a test class or a test suite
+ // -- propagate to its children.
+ mRealNotifier.fireTestSuiteStarted(child);
+ reportBeforeTestFailure(child, th);
+ mRealNotifier.fireTestSuiteFinished(child);
+ } else {
+ mRealNotifier.fireTestStarted(child);
+ Failure f = new Failure(child, th);
+ if (th instanceof AssumptionViolatedException) {
+ mRealNotifier.fireTestAssumptionFailed(f);
+ } else {
+ mRealNotifier.fireTestFailure(f);
+ }
+ mRealNotifier.fireTestFinished(child);
+ }
+ }
+ }
+
+ public void reportAfterTestFailure(Throwable th) {
+ // Unfortunately, there's no good way to report it, so kill the own process.
+ onCriticalError(
+ "Failures detected in @AfterClass, which would be swallowed by tradefed",
+ th);
+ }
+ }
+
+ private static volatile BiConsumer<String, Throwable> sCriticalErrorHanler;
+
+ private void onCriticalError(@NonNull String message, @Nullable Throwable th) {
+ Log.e(TAG, "Critical error! " + message, th);
+ var handler = sCriticalErrorHanler;
+ if (handler == null) {
+ handler = sDefaultCriticalErrorHandler;
+ }
+ handler.accept(message, th);
+ }
+
+ private static BiConsumer<String, Throwable> sDefaultCriticalErrorHandler = (message, th) -> {
+ Log.e(TAG, "Ravenwood cannot continue. Killing self process.", th);
+ System.exit(1);
+ };
+
+ /**
+ * Contains Ravenwood private APIs.
+ */
+ public static class RavenwoodPrivate {
+ private RavenwoodPrivate() {
+ }
+
+ /**
+ * Set a listener for onCriticalError(), for testing. If a listener is set, we won't call
+ * System.exit().
+ */
+ public void setCriticalErrorHandler(
+ @Nullable BiConsumer<String, Throwable> handler) {
+ sCriticalErrorHanler = handler;
+ }
+ }
+
+ private static final RavenwoodPrivate sRavenwoodPrivate = new RavenwoodPrivate();
+
+ public static RavenwoodPrivate private$ravenwood() {
+ return sRavenwoodPrivate;
+ }
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
new file mode 100644
index 0000000..04e0bed
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -0,0 +1,184 @@
+/*
+ * 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 android.platform.test.ravenwood;
+
+import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.UserHandle.SYSTEM;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Instrumentation;
+import android.content.Context;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Represents how to configure the ravenwood environment for a test class.
+ *
+ * If a ravenwood test class has a public static field with the {@link Config} annotation,
+ * Ravenwood will extract the config from it and initializes the environment. The type of the
+ * field must be of {@link RavenwoodConfig}.
+ */
+public final class RavenwoodConfig {
+ /**
+ * Use this to mark a field as the configuration.
+ * @hide
+ */
+ @Target({ElementType.FIELD})
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Config {
+ }
+
+ private static final int NOBODY_UID = 9999;
+
+ private static final AtomicInteger sNextPid = new AtomicInteger(100);
+
+ int mCurrentUser = SYSTEM.getIdentifier();
+
+ /**
+ * Unless the test author requests differently, run as "nobody", and give each collection of
+ * tests its own unique PID.
+ */
+ int mUid = NOBODY_UID;
+ int mPid = sNextPid.getAndIncrement();
+
+ String mPackageName;
+
+ boolean mProvideMainThread = false;
+
+ final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
+
+ final List<Class<?>> mServicesRequired = new ArrayList<>();
+
+ volatile Context mContext;
+ volatile Instrumentation mInstrumentation;
+
+ private RavenwoodConfig() {
+ }
+
+ /**
+ * Return if the current process is running on a Ravenwood test environment.
+ */
+ public static boolean isOnRavenwood() {
+ return RavenwoodRule.isOnRavenwood();
+ }
+
+ public static class Builder {
+ private final RavenwoodConfig mConfig = new RavenwoodConfig();
+
+ public Builder() {
+ }
+
+ /**
+ * Configure the identity of this process to be the system UID for the duration of the
+ * test. Has no effect on non-Ravenwood environments.
+ */
+ public Builder setProcessSystem() {
+ mConfig.mUid = SYSTEM_UID;
+ return this;
+ }
+
+ /**
+ * Configure the identity of this process to be an app UID for the duration of the
+ * test. Has no effect on non-Ravenwood environments.
+ */
+ public Builder setProcessApp() {
+ mConfig.mUid = FIRST_APPLICATION_UID;
+ return this;
+ }
+
+ /**
+ * Configure the identity of this process to be the given package name for the duration
+ * of the test. Has no effect on non-Ravenwood environments.
+ */
+ public Builder setPackageName(@NonNull String packageName) {
+ mConfig.mPackageName = Objects.requireNonNull(packageName);
+ return this;
+ }
+
+ /**
+ * Configure a "main" thread to be available for the duration of the test, as defined
+ * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
+ */
+ public Builder setProvideMainThread(boolean provideMainThread) {
+ mConfig.mProvideMainThread = provideMainThread;
+ return this;
+ }
+
+ /**
+ * Configure the given system property as immutable for the duration of the test.
+ * Read access to the key is allowed, and write access will fail. When {@code value} is
+ * {@code null}, the value is left as undefined.
+ *
+ * All properties in the {@code debug.*} namespace are automatically mutable, with no
+ * developer action required.
+ *
+ * Has no effect on non-Ravenwood environments.
+ */
+ public Builder setSystemPropertyImmutable(@NonNull String key,
+ @Nullable Object value) {
+ mConfig.mSystemProperties.setValue(key, value);
+ mConfig.mSystemProperties.setAccessReadOnly(key);
+ return this;
+ }
+
+ /**
+ * Configure the given system property as mutable for the duration of the test.
+ * Both read and write access to the key is allowed, and its value will be reset between
+ * each test. When {@code value} is {@code null}, the value is left as undefined.
+ *
+ * All properties in the {@code debug.*} namespace are automatically mutable, with no
+ * developer action required.
+ *
+ * Has no effect on non-Ravenwood environments.
+ */
+ public Builder setSystemPropertyMutable(@NonNull String key,
+ @Nullable Object value) {
+ mConfig.mSystemProperties.setValue(key, value);
+ mConfig.mSystemProperties.setAccessReadWrite(key);
+ return this;
+ }
+
+ /**
+ * Configure the set of system services that are required for this test to operate.
+ *
+ * For example, passing {@code android.hardware.SerialManager.class} as an argument will
+ * ensure that the underlying service is created, initialized, and ready to use for the
+ * duration of the test. The {@code SerialManager} instance can be obtained via
+ * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and
+ * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}.
+ */
+ public Builder setServicesRequired(@NonNull Class<?>... services) {
+ mConfig.mServicesRequired.clear();
+ for (Class<?> service : services) {
+ mConfig.mServicesRequired.add(service);
+ }
+ return this;
+ }
+
+ public RavenwoodConfig build() {
+ return mConfig;
+ }
+ }
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index d569896..7847e7c 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -16,17 +16,13 @@
package android.platform.test.ravenwood;
-import static android.os.Process.FIRST_APPLICATION_UID;
-import static android.os.Process.SYSTEM_UID;
-import static android.os.UserHandle.SYSTEM;
-
import static com.android.ravenwood.common.RavenwoodCommonUtils.log;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.annotations.EnabledOnRavenwood;
import com.android.ravenwood.common.RavenwoodCommonUtils;
@@ -34,26 +30,17 @@
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
/**
- * {@code @Rule} that configures the Ravenwood test environment. This rule has no effect when
- * tests are run on non-Ravenwood test environments.
- *
- * This rule initializes and resets the Ravenwood environment between each test method to offer a
- * hermetic testing environment.
- *
- * By default, all tests are executed on Ravenwood, but annotations such as
- * {@link DisabledOnRavenwood} and {@link EnabledOnRavenwood} can be used at both the method
- * and class level to "ignore" tests that may not be ready. When needed, a
- * {@link RavenwoodClassRule} can be used in addition to a {@link RavenwoodRule} to ignore tests
- * before a test class is fully initialized.
+ * @deprecated Use {@link RavenwoodConfig} to configure the ravenwood environment instead.
+ * A {@link RavenwoodRule} is no longer needed for {@link DisabledOnRavenwood}. To get the
+ * {@link Context} and {@link Instrumentation}, use
+ * {@link androidx.test.platform.app.InstrumentationRegistry} instead.
*/
-public class RavenwoodRule implements TestRule {
+@Deprecated
+public final class RavenwoodRule implements TestRule {
private static final String TAG = "RavenwoodRule";
static final boolean IS_ON_RAVENWOOD = RavenwoodCommonUtils.isOnRavenwood();
@@ -105,35 +92,19 @@
}
}
- private static final int NOBODY_UID = 9999;
-
- private static final AtomicInteger sNextPid = new AtomicInteger(100);
-
- int mCurrentUser = SYSTEM.getIdentifier();
-
- /**
- * Unless the test author requests differently, run as "nobody", and give each collection of
- * tests its own unique PID.
- */
- int mUid = NOBODY_UID;
- int mPid = sNextPid.getAndIncrement();
-
- String mPackageName;
-
- boolean mProvideMainThread = false;
-
- final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
-
- final List<Class<?>> mServicesRequired = new ArrayList<>();
-
- volatile Context mContext;
- volatile Instrumentation mInstrumentation;
+ private final RavenwoodConfig mConfiguration;
public RavenwoodRule() {
+ mConfiguration = new RavenwoodConfig.Builder().build();
+ }
+
+ private RavenwoodRule(RavenwoodConfig config) {
+ mConfiguration = config;
}
public static class Builder {
- private RavenwoodRule mRule = new RavenwoodRule();
+ private final RavenwoodConfig.Builder mBuilder =
+ new RavenwoodConfig.Builder();
public Builder() {
}
@@ -143,7 +114,7 @@
* test. Has no effect on non-Ravenwood environments.
*/
public Builder setProcessSystem() {
- mRule.mUid = SYSTEM_UID;
+ mBuilder.setProcessSystem();
return this;
}
@@ -152,7 +123,7 @@
* test. Has no effect on non-Ravenwood environments.
*/
public Builder setProcessApp() {
- mRule.mUid = FIRST_APPLICATION_UID;
+ mBuilder.setProcessApp();
return this;
}
@@ -160,8 +131,8 @@
* Configure the identity of this process to be the given package name for the duration
* of the test. Has no effect on non-Ravenwood environments.
*/
- public Builder setPackageName(/* @NonNull */ String packageName) {
- mRule.mPackageName = Objects.requireNonNull(packageName);
+ public Builder setPackageName(@NonNull String packageName) {
+ mBuilder.setPackageName(packageName);
return this;
}
@@ -170,7 +141,7 @@
* by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
*/
public Builder setProvideMainThread(boolean provideMainThread) {
- mRule.mProvideMainThread = provideMainThread;
+ mBuilder.setProvideMainThread(provideMainThread);
return this;
}
@@ -184,10 +155,8 @@
*
* Has no effect on non-Ravenwood environments.
*/
- public Builder setSystemPropertyImmutable(/* @NonNull */ String key,
- /* @Nullable */ Object value) {
- mRule.mSystemProperties.setValue(key, value);
- mRule.mSystemProperties.setAccessReadOnly(key);
+ public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) {
+ mBuilder.setSystemPropertyImmutable(key, value);
return this;
}
@@ -201,10 +170,8 @@
*
* Has no effect on non-Ravenwood environments.
*/
- public Builder setSystemPropertyMutable(/* @NonNull */ String key,
- /* @Nullable */ Object value) {
- mRule.mSystemProperties.setValue(key, value);
- mRule.mSystemProperties.setAccessReadWrite(key);
+ public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) {
+ mBuilder.setSystemPropertyMutable(key, value);
return this;
}
@@ -217,16 +184,13 @@
* {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and
* {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}.
*/
- public Builder setServicesRequired(Class<?>... services) {
- mRule.mServicesRequired.clear();
- for (Class<?> service : services) {
- mRule.mServicesRequired.add(service);
- }
+ public Builder setServicesRequired(@NonNull Class<?>... services) {
+ mBuilder.setServicesRequired(services);
return this;
}
public RavenwoodRule build() {
- return mRule;
+ return new RavenwoodRule(mBuilder.build());
}
}
@@ -246,41 +210,44 @@
}
/**
- * Return a {@code Context} available for usage during the currently running test case.
- *
- * Each test should obtain needed information or references via this method;
- * references must not be stored beyond the scope of a test case.
+ * @deprecated Use
+ * {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getContext()}
+ * instead.
*/
+ @Deprecated
public Context getContext() {
- return Objects.requireNonNull(mContext,
+ return Objects.requireNonNull(mConfiguration.mContext,
"Context is only available during @Test execution");
}
/**
- * Return a {@code Instrumentation} available for usage during the currently running test case.
- *
- * Each test should obtain needed information or references via this method;
- * references must not be stored beyond the scope of a test case.
+ * @deprecated Use
+ * {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()}
+ * instead.
*/
+ @Deprecated
public Instrumentation getInstrumentation() {
- return Objects.requireNonNull(mInstrumentation,
+ return Objects.requireNonNull(mConfiguration.mInstrumentation,
"Instrumentation is only available during @Test execution");
}
-
@Override
public Statement apply(Statement base, Description description) {
- // TODO: Here, we're calling init() / reset() once for each rule.
- // That means if a test class has multiple rules -- even if they refer to the same
- // rule instance -- we're calling them multiple times. We need to fix it.
+ if (!RavenwoodConfig.isOnRavenwood()) {
+ return base;
+ }
return new Statement() {
@Override
public void evaluate() throws Throwable {
- RavenwoodRuleImpl.init(RavenwoodRule.this);
+ RavenwoodAwareTestRunnerHook.onRavenwoodRuleEnter(
+ RavenwoodAwareTestRunner.getCurrentRunner(), description,
+ RavenwoodRule.this);
try {
base.evaluate();
} finally {
- RavenwoodRuleImpl.reset(RavenwoodRule.this);
+ RavenwoodAwareTestRunnerHook.onRavenwoodRuleExit(
+ RavenwoodAwareTestRunner.getCurrentRunner(), description,
+ RavenwoodRule.this);
}
}
};
@@ -339,4 +306,8 @@
public static RavenwoodPrivate private$ravenwood() {
return sRavenwoodPrivate;
}
+
+ RavenwoodConfig getConfiguration() {
+ return mConfiguration;
+ }
}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
index 1e4889c..0178b93 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
@@ -19,7 +19,6 @@
import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope;
import org.junit.runner.Description;
-import org.junit.runner.Runner;
import org.junit.runners.model.TestClass;
/**
@@ -38,7 +37,7 @@
/**
* Called when a runner starts, before the inner runner gets a chance to run.
*/
- public static void onRunnerInitializing(Runner runner, TestClass testClass) {
+ public static void onRunnerInitializing(RavenwoodAwareTestRunner runner, TestClass testClass) {
}
/**
@@ -48,15 +47,38 @@
}
/**
+ * Called before the inner runner starts.
+ */
+ public static void onBeforeInnerRunnerStart(
+ RavenwoodAwareTestRunner runner, Description description) throws Throwable {
+ }
+
+ /**
+ * Called after the inner runner finished.
+ */
+ public static void onAfterInnerRunnerFinished(
+ RavenwoodAwareTestRunner runner, Description description) throws Throwable {
+ }
+
+ /**
* Called before a test / class.
*
* Return false if it should be skipped.
*/
public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
- Scope scope, Order order) {
+ Scope scope, Order order) throws Throwable {
return true;
}
+ public static void onRavenwoodRuleEnter(RavenwoodAwareTestRunner runner,
+ Description description, RavenwoodRule rule) throws Throwable {
+ }
+
+ public static void onRavenwoodRuleExit(RavenwoodAwareTestRunner runner,
+ Description description, RavenwoodRule rule) throws Throwable {
+ }
+
+
/**
* Called after a test / class.
*
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
deleted file mode 100644
index a470626..0000000
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.platform.test.ravenwood;
-
-import org.junit.runner.Description;
-
-public class RavenwoodRuleImpl {
- public static void init(RavenwoodRule rule) {
- // No-op when running on a real device
- }
-
- public static void reset(RavenwoodRule rule) {
- // No-op when running on a real device
- }
-
- public static void logTestRunner(String label, Description description) {
- // No-op when running on a real device
- }
-
- public static long realCurrentTimeMillis() {
- return System.currentTimeMillis();
- }
-}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 7b5bc5a..96746c6 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -15,12 +15,18 @@
*/
package com.android.ravenwood.common;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.ravenwood.common.divergence.RavenwoodDivergence;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@@ -33,6 +39,14 @@
private static final Object sLock = new Object();
+ /**
+ * If set to "1", we enable the verbose logging.
+ *
+ * (See also InitLogging() in http://ac/system/libbase/logging.cpp)
+ */
+ public static final boolean RAVENWOOD_VERBOSE_LOGGING = "1".equals(System.getenv(
+ "RAVENWOOD_VERBOSE"));
+
/** Name of `libravenwood_runtime` */
private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
@@ -265,4 +279,24 @@
method.getDeclaringClass().getName(), method.getName(),
(isStatic ? "static " : "")));
}
+
+ public static void ensureIsPublicMember(Member member, boolean isStatic) {
+ var ok = Modifier.isPublic(member.getModifiers())
+ && (Modifier.isStatic(member.getModifiers()) == isStatic);
+ if (ok) {
+ return; // okay
+ }
+ throw new AssertionError(String.format(
+ "%s.%s expected to be public %s",
+ member.getDeclaringClass().getName(), member.getName(),
+ (isStatic ? "static" : "")));
+ }
+
+ @NonNull
+ public static String getStackTraceString(@Nullable Throwable th) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ th.printStackTrace(writer);
+ return stringWriter.toString();
+ }
}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/CursorWindow_host.java b/ravenwood/runtime-helper-src/framework/android/database/CursorWindow_host.java
similarity index 89%
rename from ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/CursorWindow_host.java
rename to ravenwood/runtime-helper-src/framework/android/database/CursorWindow_host.java
index f38d565..e21a9cd 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/CursorWindow_host.java
+++ b/ravenwood/runtime-helper-src/framework/android/database/CursorWindow_host.java
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.platform.test.ravenwood.nativesubstitution;
+package android.database;
-import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.os.Parcel;
import android.util.Base64;
@@ -35,8 +34,8 @@
private String mName;
private int mColumnNum;
private static class Row {
- String[] fields;
- int[] types;
+ String[] mFields;
+ int[] mTypes;
}
private final List<Row> mRows = new ArrayList<>();
@@ -69,9 +68,9 @@
public static boolean nativeAllocRow(long windowPtr) {
CursorWindow_host instance = sInstances.get(windowPtr);
Row row = new Row();
- row.fields = new String[instance.mColumnNum];
- row.types = new int[instance.mColumnNum];
- Arrays.fill(row.types, Cursor.FIELD_TYPE_NULL);
+ row.mFields = new String[instance.mColumnNum];
+ row.mTypes = new int[instance.mColumnNum];
+ Arrays.fill(row.mTypes, Cursor.FIELD_TYPE_NULL);
instance.mRows.add(row);
return true;
}
@@ -82,8 +81,8 @@
return false;
}
Row r = instance.mRows.get(row);
- r.fields[column] = value;
- r.types[column] = type;
+ r.mFields[column] = value;
+ r.mTypes[column] = type;
return true;
}
@@ -93,7 +92,7 @@
return Cursor.FIELD_TYPE_NULL;
}
- return instance.mRows.get(row).types[column];
+ return instance.mRows.get(row).mTypes[column];
}
public static boolean nativePutString(long windowPtr, String value,
@@ -107,7 +106,7 @@
return null;
}
- return instance.mRows.get(row).fields[column];
+ return instance.mRows.get(row).mFields[column];
}
public static boolean nativePutLong(long windowPtr, long value, int row, int column) {
@@ -170,8 +169,8 @@
parcel.writeInt(window.mColumnNum);
parcel.writeInt(window.mRows.size());
for (int row = 0; row < window.mRows.size(); row++) {
- parcel.writeStringArray(window.mRows.get(row).fields);
- parcel.writeIntArray(window.mRows.get(row).types);
+ parcel.writeStringArray(window.mRows.get(row).mFields);
+ parcel.writeIntArray(window.mRows.get(row).mTypes);
}
}
@@ -183,8 +182,8 @@
int rowCount = parcel.readInt();
for (int row = 0; row < rowCount; row++) {
Row r = new Row();
- r.fields = parcel.createStringArray();
- r.types = parcel.createIntArray();
+ r.mFields = parcel.createStringArray();
+ r.mTypes = parcel.createIntArray();
window.mRows.add(r);
}
return windowPtr;
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/MessageQueue_host.java b/ravenwood/runtime-helper-src/framework/android/os/MessageQueue_host.java
similarity index 97%
rename from ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/MessageQueue_host.java
rename to ravenwood/runtime-helper-src/framework/android/os/MessageQueue_host.java
index 5e81124..1b63adc 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/MessageQueue_host.java
+++ b/ravenwood/runtime-helper-src/framework/android/os/MessageQueue_host.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.platform.test.ravenwood.nativesubstitution;
+package android.os;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/SystemProperties_host.java b/ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java
similarity index 89%
rename from ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/SystemProperties_host.java
rename to ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java
index e7479d3..b09bf31 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/SystemProperties_host.java
+++ b/ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.platform.test.ravenwood.nativesubstitution;
+package android.os;
import android.util.SparseArray;
@@ -36,9 +36,6 @@
/** Predicate tested to determine if a given key can be written. */
@GuardedBy("sLock")
private static Predicate<String> sKeyWritablePredicate;
- /** Callback to trigger when values are changed */
- @GuardedBy("sLock")
- private static Runnable sChangeCallback;
/**
* Reverse mapping that provides a way back to an original key from the
@@ -48,7 +45,7 @@
private static SparseArray<String> sKeyHandles = new SparseArray<>();
/**
- * Basically the same as {@link #native_init$ravenwood}, but it'll only run if no values are
+ * Basically the same as {@link #init$ravenwood}, but it'll only run if no values are
* set yet.
*/
public static void initializeIfNeeded(Map<String, String> values,
@@ -57,30 +54,32 @@
if (sValues != null) {
return; // Already initialized.
}
- native_init$ravenwood(values, keyReadablePredicate, keyWritablePredicate,
- () -> {});
+ init$ravenwood(values, keyReadablePredicate, keyWritablePredicate);
}
}
- public static void native_init$ravenwood(Map<String, String> values,
- Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate,
- Runnable changeCallback) {
+ public static void init$ravenwood(Map<String, String> values,
+ Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate) {
synchronized (sLock) {
sValues = Objects.requireNonNull(values);
sKeyReadablePredicate = Objects.requireNonNull(keyReadablePredicate);
sKeyWritablePredicate = Objects.requireNonNull(keyWritablePredicate);
- sChangeCallback = Objects.requireNonNull(changeCallback);
sKeyHandles.clear();
+ synchronized (SystemProperties.sChangeCallbacks) {
+ SystemProperties.sChangeCallbacks.clear();
+ }
}
}
- public static void native_reset$ravenwood() {
+ public static void reset$ravenwood() {
synchronized (sLock) {
sValues = null;
sKeyReadablePredicate = null;
sKeyWritablePredicate = null;
- sChangeCallback = null;
sKeyHandles.clear();
+ synchronized (SystemProperties.sChangeCallbacks) {
+ SystemProperties.sChangeCallbacks.clear();
+ }
}
}
@@ -101,7 +100,7 @@
} else {
sValues.put(key, val);
}
- sChangeCallback.run();
+ SystemProperties.callChangeCallbacks();
}
}
@@ -183,7 +182,7 @@
// Report through callback always registered via init above
synchronized (sLock) {
Preconditions.requireNonNullViaRavenwoodRule(sValues);
- sChangeCallback.run();
+ SystemProperties.callChangeCallbacks();
}
}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/EventLog_host.java b/ravenwood/runtime-helper-src/framework/android/util/EventLog_host.java
similarity index 83%
rename from ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/EventLog_host.java
rename to ravenwood/runtime-helper-src/framework/android/util/EventLog_host.java
index 55d4ffb..878a0ff 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/EventLog_host.java
+++ b/ravenwood/runtime-helper-src/framework/android/util/EventLog_host.java
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.platform.test.ravenwood.nativesubstitution;
+package android.util;
import com.android.internal.os.RuntimeInit;
import java.io.PrintStream;
-import java.util.Collection;
public class EventLog_host {
public static int writeEvent(int tag, int value) {
@@ -58,15 +57,6 @@
return sb.length();
}
- public static void readEvents(int[] tags, Collection<android.util.EventLog.Event> output) {
- throw new UnsupportedOperationException();
- }
-
- public static void readEventsOnWrapping(int[] tags, long timestamp,
- Collection<android.util.EventLog.Event> output) {
- throw new UnsupportedOperationException();
- }
-
/**
* Return the "real" {@code System.out} if it's been swapped by {@code RavenwoodRuleImpl}, so
* that we don't end up in a recursive loop.
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Log_host.java b/ravenwood/runtime-helper-src/framework/android/util/Log_host.java
similarity index 95%
rename from ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Log_host.java
rename to ravenwood/runtime-helper-src/framework/android/util/Log_host.java
index f301b9c..d232ef2 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Log_host.java
+++ b/ravenwood/runtime-helper-src/framework/android/util/Log_host.java
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.platform.test.ravenwood.nativesubstitution;
+package android.util;
-import android.util.Log;
import android.util.Log.Level;
import com.android.internal.os.RuntimeInit;
@@ -44,7 +43,7 @@
case Log.LOG_ID_SYSTEM: buffer = "system"; break;
case Log.LOG_ID_CRASH: buffer = "crash"; break;
default: buffer = "buf:" + bufID; break;
- };
+ }
final String prio;
switch (priority) {
@@ -55,7 +54,7 @@
case Log.ERROR: prio = "E"; break;
case Log.ASSERT: prio = "A"; break;
default: prio = "prio:" + priority; break;
- };
+ }
for (String s : msg.split("\\n")) {
getRealOut().println(String.format("logd: [%s] %s %s: %s", buffer, prio, tag, s));
diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayContainer_host.java b/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayContainer_host.java
new file mode 100644
index 0000000..c18c307
--- /dev/null
+++ b/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayContainer_host.java
@@ -0,0 +1,63 @@
+/*
+ * 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.os;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class LongArrayContainer_host {
+ private static final HashMap<Long, long[]> sInstances = new HashMap<>();
+ private static long sNextId = 1;
+
+ public static long native_init(int arrayLength) {
+ long[] array = new long[arrayLength];
+ long instanceId = sNextId++;
+ sInstances.put(instanceId, array);
+ return instanceId;
+ }
+
+ static long[] getInstance(long instanceId) {
+ return sInstances.get(instanceId);
+ }
+
+ public static void native_setValues(long instanceId, long[] values) {
+ System.arraycopy(values, 0, getInstance(instanceId), 0, values.length);
+ }
+
+ public static void native_getValues(long instanceId, long[] values) {
+ System.arraycopy(getInstance(instanceId), 0, values, 0, values.length);
+ }
+
+ public static boolean native_combineValues(long instanceId, long[] array, int[] indexMap) {
+ long[] values = getInstance(instanceId);
+
+ boolean nonZero = false;
+ Arrays.fill(array, 0);
+
+ for (int i = 0; i < values.length; i++) {
+ int index = indexMap[i];
+ if (index < 0 || index >= array.length) {
+ throw new IndexOutOfBoundsException("Index " + index + " is out of bounds: [0, "
+ + (array.length - 1) + "]");
+ }
+ if (values[i] != 0) {
+ array[index] += values[i];
+ nonZero = true;
+ }
+ }
+ return nonZero;
+ }
+}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayMultiStateCounter_host.java
similarity index 87%
rename from ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
rename to ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayMultiStateCounter_host.java
index 0f65544..9ce8ea8 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayMultiStateCounter_host.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.platform.test.ravenwood.nativesubstitution;
+package com.android.internal.os;
import android.os.BadParcelableException;
import android.os.Parcel;
@@ -28,7 +28,7 @@
public class LongArrayMultiStateCounter_host {
/**
- * A reimplementation of {@link com.android.internal.os.LongArrayMultiStateCounter}, only in
+ * A reimplementation of {@link LongArrayMultiStateCounter}, only in
* Java instead of native. The majority of the code (in C++) can be found in
* /frameworks/native/libs/battery/MultiStateCounter.h
*/
@@ -257,50 +257,6 @@
}
}
- public static class LongArrayContainer_host {
- private static final HashMap<Long, long[]> sInstances = new HashMap<>();
- private static long sNextId = 1;
-
- public static long native_init(int arrayLength) {
- long[] array = new long[arrayLength];
- long instanceId = sNextId++;
- sInstances.put(instanceId, array);
- return instanceId;
- }
-
- static long[] getInstance(long instanceId) {
- return sInstances.get(instanceId);
- }
-
- public static void native_setValues(long instanceId, long[] values) {
- System.arraycopy(values, 0, getInstance(instanceId), 0, values.length);
- }
-
- public static void native_getValues(long instanceId, long[] values) {
- System.arraycopy(getInstance(instanceId), 0, values, 0, values.length);
- }
-
- public static boolean native_combineValues(long instanceId, long[] array, int[] indexMap) {
- long[] values = getInstance(instanceId);
-
- boolean nonZero = false;
- Arrays.fill(array, 0);
-
- for (int i = 0; i < values.length; i++) {
- int index = indexMap[i];
- if (index < 0 || index >= array.length) {
- throw new IndexOutOfBoundsException("Index " + index + " is out of bounds: [0, "
- + (array.length - 1) + "]");
- }
- if (values[i] != 0) {
- array[index] += values[i];
- nonZero = true;
- }
- }
- return nonZero;
- }
- }
-
private static final HashMap<Long, LongArrayMultiStateCounterRavenwood> sInstances =
new HashMap<>();
private static long sNextId = 1;
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongMultiStateCounter_host.java
similarity index 98%
rename from ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongMultiStateCounter_host.java
rename to ravenwood/runtime-helper-src/framework/com/android/internal/os/LongMultiStateCounter_host.java
index 9486651..1d95aa1 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongMultiStateCounter_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongMultiStateCounter_host.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.platform.test.ravenwood.nativesubstitution;
+package com.android.internal.os;
import android.os.BadParcelableException;
import android.os.Parcel;
diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java b/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java
new file mode 100644
index 0000000..e12ff24
--- /dev/null
+++ b/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ravenwood;
+
+import com.android.ravenwood.common.JvmWorkaround;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+public class RavenwoodEnvironment_host {
+ private RavenwoodEnvironment_host() {
+ }
+
+ /**
+ * Called from {@link RavenwoodEnvironment#ensureRavenwoodInitialized()}.
+ */
+ public static void ensureRavenwoodInitialized() {
+ // Initialization is now done by RavenwoodAwareTestRunner.
+ // Should we remove it?
+ }
+
+ /**
+ * Called from {@link RavenwoodEnvironment#getRavenwoodRuntimePath()}.
+ */
+ public static String getRavenwoodRuntimePath(RavenwoodEnvironment env) {
+ return RavenwoodCommonUtils.getRavenwoodRuntimePath();
+ }
+
+ /**
+ * Called from {@link RavenwoodEnvironment#fromAddress(long)}.
+ */
+ public static <T> T fromAddress(RavenwoodEnvironment env, long address) {
+ return JvmWorkaround.getInstance().fromAddress(address);
+ }
+}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
deleted file mode 100644
index cb00b3e..0000000
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * Copyright (C) 2023 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.platform.test.ravenwood.nativesubstitution;
-
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * Tentative, partial implementation of the Parcel native methods, using Java's
- * {@code byte[]}.
- * (We don't use a {@link ByteBuffer} because there's enough semantics differences between Parcel
- * and {@link ByteBuffer}, and it didn't work out.
- * e.g. Parcel seems to allow moving the data position to be beyond its size? Which
- * {@link ByteBuffer} wouldn't allow...)
- */
-public class Parcel_host {
- private static final String TAG = "Parcel";
-
- private Parcel_host() {
- }
-
- private static final AtomicLong sNextId = new AtomicLong(1);
-
- private static final Map<Long, Parcel_host> sInstances = new ConcurrentHashMap<>();
-
- private boolean mDeleted = false;
-
- private byte[] mBuffer;
- private int mSize;
- private int mPos;
-
- private boolean mSensitive;
- private boolean mAllowFds;
-
- // TODO Use the actual value from Parcel.java.
- private static final int OK = 0;
-
- private final Map<Integer, FileDescriptor> mFdMap = new ConcurrentHashMap<>();
-
- private static final int FD_PLACEHOLDER = 0xDEADBEEF;
- private static final int FD_PAYLOAD_SIZE = 8;
-
- private void validate() {
- if (mDeleted) {
- // TODO: Put more info
- throw new RuntimeException("Parcel already destroyed");
- }
- }
-
- private static Parcel_host getInstance(long id) {
- Parcel_host p = sInstances.get(id);
- if (p == null) {
- // TODO: Put more info
- throw new RuntimeException("Parcel doesn't exist with id=" + id);
- }
- p.validate();
- return p;
- }
-
- /** Native method substitution */
- public static long nativeCreate() {
- final long id = sNextId.getAndIncrement();
- final Parcel_host p = new Parcel_host();
- sInstances.put(id, p);
- p.init();
- return id;
- }
-
- private void init() {
- mBuffer = new byte[0];
- mSize = 0;
- mPos = 0;
- mSensitive = false;
- mAllowFds = true;
- mFdMap.clear();
- }
-
- private void updateSize() {
- if (mSize < mPos) {
- mSize = mPos;
- }
- }
-
- /** Native method substitution */
- public static void nativeDestroy(long nativePtr) {
- getInstance(nativePtr).mDeleted = true;
- sInstances.remove(nativePtr);
- }
-
- /** Native method substitution */
- public static void nativeFreeBuffer(long nativePtr) {
- getInstance(nativePtr).freeBuffer();
- }
-
- /** Native method substitution */
- private void freeBuffer() {
- init();
- }
-
- private int getCapacity() {
- return mBuffer.length;
- }
-
- private void ensureMoreCapacity(int size) {
- ensureCapacity(mPos + size);
- }
-
- private void ensureCapacity(int targetSize) {
- if (targetSize <= getCapacity()) {
- return;
- }
- var newSize = getCapacity() * 2;
- if (newSize < targetSize) {
- newSize = targetSize;
- }
- forceSetCapacity(newSize);
- }
-
- private void forceSetCapacity(int newSize) {
- var newBuf = new byte[newSize];
-
- // Copy
- System.arraycopy(mBuffer, 0, newBuf, 0, Math.min(newSize, getCapacity()));
-
- this.mBuffer = newBuf;
- }
-
- private void ensureDataAvailable(int requestSize) {
- if (mSize - mPos < requestSize) {
- throw new RuntimeException(String.format(
- "Pacel data underflow. size=%d, pos=%d, request=%d", mSize, mPos, requestSize));
- }
- }
-
- /** Native method substitution */
- public static void nativeMarkSensitive(long nativePtr) {
- getInstance(nativePtr).mSensitive = true;
- }
-
- /** Native method substitution */
- public static int nativeDataSize(long nativePtr) {
- return getInstance(nativePtr).mSize;
- }
-
- /** Native method substitution */
- public static int nativeDataAvail(long nativePtr) {
- var p = getInstance(nativePtr);
- return p.mSize - p.mPos;
- }
-
- /** Native method substitution */
- public static int nativeDataPosition(long nativePtr) {
- return getInstance(nativePtr).mPos;
- }
-
- /** Native method substitution */
- public static int nativeDataCapacity(long nativePtr) {
- return getInstance(nativePtr).mBuffer.length;
- }
-
- /** Native method substitution */
- public static void nativeSetDataSize(long nativePtr, int size) {
- var p = getInstance(nativePtr);
- p.ensureCapacity(size);
- getInstance(nativePtr).mSize = size;
- }
-
- /** Native method substitution */
- public static void nativeSetDataPosition(long nativePtr, int pos) {
- var p = getInstance(nativePtr);
- // TODO: Should this change the size or the capacity??
- p.mPos = pos;
- }
-
- /** Native method substitution */
- public static void nativeSetDataCapacity(long nativePtr, int size) {
- if (size < 0) {
- throw new IllegalArgumentException("size < 0: size=" + size);
- }
- var p = getInstance(nativePtr);
- if (p.getCapacity() < size) {
- p.forceSetCapacity(size);
- }
- }
-
- /** Native method substitution */
- public static boolean nativePushAllowFds(long nativePtr, boolean allowFds) {
- var p = getInstance(nativePtr);
- var prev = p.mAllowFds;
- p.mAllowFds = allowFds;
- return prev;
- }
-
- /** Native method substitution */
- public static void nativeRestoreAllowFds(long nativePtr, boolean lastValue) {
- getInstance(nativePtr).mAllowFds = lastValue;
- }
-
- /** Native method substitution */
- public static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) {
- nativeWriteBlob(nativePtr, b, offset, len);
- }
-
- /** Native method substitution */
- public static void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len) {
- var p = getInstance(nativePtr);
-
- if (b == null) {
- nativeWriteInt(nativePtr, -1);
- } else {
- final var alignedSize = align4(len);
-
- nativeWriteInt(nativePtr, len);
-
- p.ensureMoreCapacity(alignedSize);
-
- System.arraycopy(b, offset, p.mBuffer, p.mPos, len);
- p.mPos += alignedSize;
- p.updateSize();
- }
- }
-
- /** Native method substitution */
- public static int nativeWriteInt(long nativePtr, int value) {
- var p = getInstance(nativePtr);
- p.ensureMoreCapacity(Integer.BYTES);
-
- p.mBuffer[p.mPos++] = (byte) ((value >> 24) & 0xff);
- p.mBuffer[p.mPos++] = (byte) ((value >> 16) & 0xff);
- p.mBuffer[p.mPos++] = (byte) ((value >> 8) & 0xff);
- p.mBuffer[p.mPos++] = (byte) ((value >> 0) & 0xff);
-
- p.updateSize();
-
- return OK;
- }
-
- /** Native method substitution */
- public static int nativeWriteLong(long nativePtr, long value) {
- nativeWriteInt(nativePtr, (int) (value >>> 32));
- nativeWriteInt(nativePtr, (int) (value));
- return OK;
- }
-
- /** Native method substitution */
- public static int nativeWriteFloat(long nativePtr, float val) {
- return nativeWriteInt(nativePtr, Float.floatToIntBits(val));
- }
-
- /** Native method substitution */
- public static int nativeWriteDouble(long nativePtr, double val) {
- return nativeWriteLong(nativePtr, Double.doubleToLongBits(val));
- }
-
- private static int align4(int val) {
- return ((val + 3) / 4) * 4;
- }
-
- /** Native method substitution */
- public static void nativeWriteString8(long nativePtr, String val) {
- if (val == null) {
- nativeWriteBlob(nativePtr, null, 0, 0);
- } else {
- var bytes = val.getBytes(StandardCharsets.UTF_8);
- nativeWriteBlob(nativePtr, bytes, 0, bytes.length);
- }
- }
-
- /** Native method substitution */
- public static void nativeWriteString16(long nativePtr, String val) {
- // Just reuse String8
- nativeWriteString8(nativePtr, val);
- }
-
- /** Native method substitution */
- public static byte[] nativeCreateByteArray(long nativePtr) {
- return nativeReadBlob(nativePtr);
- }
-
- /** Native method substitution */
- public static boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen) {
- if (dest == null) {
- return false;
- }
- var data = nativeReadBlob(nativePtr);
- if (data == null) {
- System.err.println("Percel has NULL, which is unexpected."); // TODO: Is this correct?
- return false;
- }
- // TODO: Make sure the check logic is correct.
- if (data.length != destLen) {
- System.err.println("Byte array size mismatch: expected="
- + data.length + " given=" + destLen);
- return false;
- }
- System.arraycopy(data, 0, dest, 0, data.length);
- return true;
- }
-
- /** Native method substitution */
- public static byte[] nativeReadBlob(long nativePtr) {
- var p = getInstance(nativePtr);
- if (p.mSize - p.mPos < 4) {
- // Match native impl that returns "null" when not enough data
- return null;
- }
- final var size = nativeReadInt(nativePtr);
- if (size == -1) {
- return null;
- }
- try {
- p.ensureDataAvailable(align4(size));
- } catch (Exception e) {
- System.err.println(e.toString());
- return null;
- }
-
- var bytes = new byte[size];
- System.arraycopy(p.mBuffer, p.mPos, bytes, 0, size);
-
- p.mPos += align4(size);
-
- return bytes;
- }
-
- /** Native method substitution */
- public static int nativeReadInt(long nativePtr) {
- var p = getInstance(nativePtr);
-
- if (p.mSize - p.mPos < 4) {
- // Match native impl that returns "0" when not enough data
- return 0;
- }
-
- var ret = (((p.mBuffer[p.mPos++] & 0xff) << 24)
- | ((p.mBuffer[p.mPos++] & 0xff) << 16)
- | ((p.mBuffer[p.mPos++] & 0xff) << 8)
- | ((p.mBuffer[p.mPos++] & 0xff) << 0));
-
- return ret;
- }
-
- /** Native method substitution */
- public static long nativeReadLong(long nativePtr) {
- return (((long) nativeReadInt(nativePtr)) << 32)
- | (((long) nativeReadInt(nativePtr)) & 0xffff_ffffL);
- }
-
- /** Native method substitution */
- public static float nativeReadFloat(long nativePtr) {
- return Float.intBitsToFloat(nativeReadInt(nativePtr));
- }
-
- /** Native method substitution */
- public static double nativeReadDouble(long nativePtr) {
- return Double.longBitsToDouble(nativeReadLong(nativePtr));
- }
-
- /** Native method substitution */
- public static String nativeReadString8(long nativePtr) {
- final var bytes = nativeReadBlob(nativePtr);
- if (bytes == null) {
- return null;
- }
- return new String(bytes, StandardCharsets.UTF_8);
- }
- public static String nativeReadString16(long nativePtr) {
- return nativeReadString8(nativePtr);
- }
-
- /** Native method substitution */
- public static byte[] nativeMarshall(long nativePtr) {
- var p = getInstance(nativePtr);
- return Arrays.copyOf(p.mBuffer, p.mSize);
- }
-
- /** Native method substitution */
- public static void nativeUnmarshall(
- long nativePtr, byte[] data, int offset, int length) {
- var p = getInstance(nativePtr);
- p.ensureMoreCapacity(length);
- System.arraycopy(data, offset, p.mBuffer, p.mPos, length);
- p.mPos += length;
- p.updateSize();
- }
-
- /** Native method substitution */
- public static int nativeCompareData(long thisNativePtr, long otherNativePtr) {
- var a = getInstance(thisNativePtr);
- var b = getInstance(otherNativePtr);
- if ((a.mSize == b.mSize) && Arrays.equals(a.mBuffer, b.mBuffer)) {
- return 0;
- } else {
- return -1;
- }
- }
-
- /** Native method substitution */
- public static boolean nativeCompareDataInRange(
- long ptrA, int offsetA, long ptrB, int offsetB, int length) {
- var a = getInstance(ptrA);
- var b = getInstance(ptrB);
- if (offsetA < 0 || offsetA + length > a.mSize) {
- throw new IllegalArgumentException();
- }
- if (offsetB < 0 || offsetB + length > b.mSize) {
- throw new IllegalArgumentException();
- }
- return Arrays.equals(Arrays.copyOfRange(a.mBuffer, offsetA, offsetA + length),
- Arrays.copyOfRange(b.mBuffer, offsetB, offsetB + length));
- }
-
- /** Native method substitution */
- public static void nativeAppendFrom(
- long thisNativePtr, long otherNativePtr, int srcOffset, int length) {
- var dst = getInstance(thisNativePtr);
- var src = getInstance(otherNativePtr);
-
- dst.ensureMoreCapacity(length);
-
- System.arraycopy(src.mBuffer, srcOffset, dst.mBuffer, dst.mPos, length);
- dst.mPos += length; // TODO: 4 byte align?
- dst.updateSize();
-
- // TODO: Update the other's position?
- }
-
- /** Native method substitution */
- public static boolean nativeHasBinders(long nativePtr) {
- // Assume false for now, because we don't support adding binders.
- return false;
- }
-
- /** Native method substitution */
- public static boolean nativeHasBindersInRange(
- long nativePtr, int offset, int length) {
- // Assume false for now, because we don't support writing FDs yet.
- return false;
- }
-
- /** Native method substitution */
- public static void nativeWriteFileDescriptor(long nativePtr, java.io.FileDescriptor val) {
- var p = getInstance(nativePtr);
-
- if (!p.mAllowFds) {
- // Simulate the FDS_NOT_ALLOWED case in frameworks/base/core/jni/android_util_Binder.cpp
- throw new RuntimeException("Not allowed to write file descriptors here");
- }
-
- FileDescriptor dup = null;
- try {
- dup = Os.dup(val);
- } catch (ErrnoException e) {
- throw new RuntimeException(e);
- }
- p.mFdMap.put(p.mPos, dup);
-
- // Parcel.cpp writes two int32s for a FD.
- // Make sure FD_PAYLOAD_SIZE is in sync with this code.
- nativeWriteInt(nativePtr, FD_PLACEHOLDER);
- nativeWriteInt(nativePtr, FD_PLACEHOLDER);
- }
-
- /** Native method substitution */
- public static java.io.FileDescriptor nativeReadFileDescriptor(long nativePtr) {
- var p = getInstance(nativePtr);
-
- var pos = p.mPos;
- var fd = p.mFdMap.get(pos);
-
- if (fd == null) {
- Log.w(TAG, "nativeReadFileDescriptor: Not a FD at pos #" + pos);
- return null;
- }
- nativeReadInt(nativePtr);
- nativeReadInt(nativePtr);
- return fd;
- }
-
- /** Native method substitution */
- public static boolean nativeHasFileDescriptors(long nativePtr) {
- var p = getInstance(nativePtr);
- return p.mFdMap.size() > 0;
- }
-
- /** Native method substitution */
- public static boolean nativeHasFileDescriptorsInRange(long nativePtr, int offset, int length) {
- var p = getInstance(nativePtr);
-
- // Original code: hasFileDescriptorsInRange() in frameworks/native/libs/binder/Parcel.cpp
- if (offset < 0 || length < 0) {
- throw new IllegalArgumentException("Negative value not allowed: offset=" + offset
- + " length=" + length);
- }
- long limit = (long) offset + (long) length;
- if (limit > p.mSize) {
- throw new IllegalArgumentException("Out of range: offset=" + offset
- + " length=" + length + " dataSize=" + p.mSize);
- }
-
- for (var pos : p.mFdMap.keySet()) {
- if (offset <= pos && (pos + FD_PAYLOAD_SIZE - 1) < (offset + length)) {
- return true;
- }
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
deleted file mode 100644
index f894b0e..0000000
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
+++ /dev/null
@@ -1,76 +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.platform.test.ravenwood.nativesubstitution;
-
-import android.platform.test.ravenwood.RavenwoodSystemProperties;
-import android.util.Log;
-
-import com.android.internal.ravenwood.RavenwoodEnvironment;
-import com.android.ravenwood.common.JvmWorkaround;
-import com.android.ravenwood.common.RavenwoodCommonUtils;
-
-public class RavenwoodEnvironment_host {
- private static final String TAG = RavenwoodEnvironment.TAG;
-
- private static final Object sInitializeLock = new Object();
-
- // @GuardedBy("sInitializeLock")
- private static boolean sInitialized;
-
- private RavenwoodEnvironment_host() {
- }
-
- /**
- * Called from {@link RavenwoodEnvironment#ensureRavenwoodInitialized()}.
- */
- public static void ensureRavenwoodInitialized() {
-
- // TODO Unify it with the initialization code in RavenwoodAwareTestRunnerHook.
-
- synchronized (sInitializeLock) {
- if (sInitialized) {
- return;
- }
- Log.i(TAG, "Initializing Ravenwood environment");
-
- // Set the default values.
- var sysProps = RavenwoodSystemProperties.DEFAULT_VALUES;
-
- // We have a method that does it in RavenwoodRuleImpl, but we can't use that class
- // here, So just inline it.
- SystemProperties_host.initializeIfNeeded(
- sysProps.getValues(),
- sysProps.getKeyReadablePredicate(),
- sysProps.getKeyWritablePredicate());
-
- sInitialized = true;
- }
- }
-
- /**
- * Called from {@link RavenwoodEnvironment#getRavenwoodRuntimePath()}.
- */
- public static String getRavenwoodRuntimePath(RavenwoodEnvironment env) {
- return RavenwoodCommonUtils.getRavenwoodRuntimePath();
- }
-
- /**
- * Called from {@link RavenwoodEnvironment#fromAddress(long)}.
- */
- public static <T> T fromAddress(RavenwoodEnvironment env, long address) {
- return JvmWorkaround.getInstance().fromAddress(address);
- }
-}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index 0f955e7..790bb1c 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -15,6 +15,11 @@
*/
package com.android.platform.test.ravenwood.runtimehelper;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
import com.android.ravenwood.common.RavenwoodCommonUtils;
import java.io.File;
@@ -123,6 +128,15 @@
return;
}
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ log("Force enabling verbose logging");
+ try {
+ Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
+ } catch (ErrnoException e) {
+ // Shouldn't happen.
+ }
+ }
+
// Make sure these properties are not set.
ensurePropertyNotSet(CORE_NATIVE_CLASSES);
ensurePropertyNotSet(ICU_DATA_PATH);
@@ -152,6 +166,7 @@
private static final Class<?>[] sLibandroidClasses = {
android.util.Log.class,
android.os.Parcel.class,
+ android.os.Binder.class,
android.content.res.ApkAssets.class,
android.content.res.AssetManager.class,
android.content.res.StringBlock.class,
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
index a5c0b54..c94ef31 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
@@ -93,4 +93,8 @@
throw new ErrnoException("pread", OsConstants.EIO, e);
}
}
+
+ public static void setenv(String name, String value, boolean overwrite) throws ErrnoException {
+ RavenwoodRuntimeNative.setenv(name, value, overwrite);
+ }
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index 0d8408c..ad80d92 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -53,6 +53,9 @@
private static native int nOpen(String path, int flags, int mode) throws ErrnoException;
+ public static native void setenv(String name, String value, boolean overwrite)
+ throws ErrnoException;
+
public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java
new file mode 100644
index 0000000..478503b
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2019 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 libcore.util;
+
+/**
+ * <p>The {@code FP16} class is a wrapper and a utility class to manipulate half-precision 16-bit
+ * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
+ * floating point data types (also called fp16 or binary16). A half-precision float can be
+ * created from or converted to single-precision floats, and is stored in a short data type.
+ *
+ * <p>The IEEE 754 standard specifies an fp16 as having the following format:</p>
+ * <ul>
+ * <li>Sign bit: 1 bit</li>
+ * <li>Exponent width: 5 bits</li>
+ * <li>Significand: 10 bits</li>
+ * </ul>
+ *
+ * <p>The format is laid out as follows:</p>
+ * <pre>
+ * 1 11111 1111111111
+ * ^ --^-- -----^----
+ * sign | |_______ significand
+ * |
+ * -- exponent
+ * </pre>
+ *
+ * <p>Half-precision floating points can be useful to save memory and/or
+ * bandwidth at the expense of range and precision when compared to single-precision
+ * floating points (fp32).</p>
+ * <p>To help you decide whether fp16 is the right storage type for you need, please
+ * refer to the table below that shows the available precision throughout the range of
+ * possible values. The <em>precision</em> column indicates the step size between two
+ * consecutive numbers in a specific part of the range.</p>
+ *
+ * <table summary="Precision of fp16 across the range">
+ * <tr><th>Range start</th><th>Precision</th></tr>
+ * <tr><td>0</td><td>1 ⁄ 16,777,216</td></tr>
+ * <tr><td>1 ⁄ 16,384</td><td>1 ⁄ 16,777,216</td></tr>
+ * <tr><td>1 ⁄ 8,192</td><td>1 ⁄ 8,388,608</td></tr>
+ * <tr><td>1 ⁄ 4,096</td><td>1 ⁄ 4,194,304</td></tr>
+ * <tr><td>1 ⁄ 2,048</td><td>1 ⁄ 2,097,152</td></tr>
+ * <tr><td>1 ⁄ 1,024</td><td>1 ⁄ 1,048,576</td></tr>
+ * <tr><td>1 ⁄ 512</td><td>1 ⁄ 524,288</td></tr>
+ * <tr><td>1 ⁄ 256</td><td>1 ⁄ 262,144</td></tr>
+ * <tr><td>1 ⁄ 128</td><td>1 ⁄ 131,072</td></tr>
+ * <tr><td>1 ⁄ 64</td><td>1 ⁄ 65,536</td></tr>
+ * <tr><td>1 ⁄ 32</td><td>1 ⁄ 32,768</td></tr>
+ * <tr><td>1 ⁄ 16</td><td>1 ⁄ 16,384</td></tr>
+ * <tr><td>1 ⁄ 8</td><td>1 ⁄ 8,192</td></tr>
+ * <tr><td>1 ⁄ 4</td><td>1 ⁄ 4,096</td></tr>
+ * <tr><td>1 ⁄ 2</td><td>1 ⁄ 2,048</td></tr>
+ * <tr><td>1</td><td>1 ⁄ 1,024</td></tr>
+ * <tr><td>2</td><td>1 ⁄ 512</td></tr>
+ * <tr><td>4</td><td>1 ⁄ 256</td></tr>
+ * <tr><td>8</td><td>1 ⁄ 128</td></tr>
+ * <tr><td>16</td><td>1 ⁄ 64</td></tr>
+ * <tr><td>32</td><td>1 ⁄ 32</td></tr>
+ * <tr><td>64</td><td>1 ⁄ 16</td></tr>
+ * <tr><td>128</td><td>1 ⁄ 8</td></tr>
+ * <tr><td>256</td><td>1 ⁄ 4</td></tr>
+ * <tr><td>512</td><td>1 ⁄ 2</td></tr>
+ * <tr><td>1,024</td><td>1</td></tr>
+ * <tr><td>2,048</td><td>2</td></tr>
+ * <tr><td>4,096</td><td>4</td></tr>
+ * <tr><td>8,192</td><td>8</td></tr>
+ * <tr><td>16,384</td><td>16</td></tr>
+ * <tr><td>32,768</td><td>32</td></tr>
+ * </table>
+ *
+ * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p>
+ *
+ * @hide
+ */
+
+public final class FP16 {
+ /**
+ * The number of bits used to represent a half-precision float value.
+ *
+ * @hide
+ */
+ public static final int SIZE = 16;
+
+ /**
+ * Epsilon is the difference between 1.0 and the next value representable
+ * by a half-precision floating-point.
+ *
+ * @hide
+ */
+ public static final short EPSILON = (short) 0x1400;
+
+ /**
+ * Maximum exponent a finite half-precision float may have.
+ *
+ * @hide
+ */
+ public static final int MAX_EXPONENT = 15;
+ /**
+ * Minimum exponent a normalized half-precision float may have.
+ *
+ * @hide
+ */
+ public static final int MIN_EXPONENT = -14;
+
+ /**
+ * Smallest negative value a half-precision float may have.
+ *
+ * @hide
+ */
+ public static final short LOWEST_VALUE = (short) 0xfbff;
+ /**
+ * Maximum positive finite value a half-precision float may have.
+ *
+ * @hide
+ */
+ public static final short MAX_VALUE = (short) 0x7bff;
+ /**
+ * Smallest positive normal value a half-precision float may have.
+ *
+ * @hide
+ */
+ public static final short MIN_NORMAL = (short) 0x0400;
+ /**
+ * Smallest positive non-zero value a half-precision float may have.
+ *
+ * @hide
+ */
+ public static final short MIN_VALUE = (short) 0x0001;
+ /**
+ * A Not-a-Number representation of a half-precision float.
+ *
+ * @hide
+ */
+ public static final short NaN = (short) 0x7e00;
+ /**
+ * Negative infinity of type half-precision float.
+ *
+ * @hide
+ */
+ public static final short NEGATIVE_INFINITY = (short) 0xfc00;
+ /**
+ * Negative 0 of type half-precision float.
+ *
+ * @hide
+ */
+ public static final short NEGATIVE_ZERO = (short) 0x8000;
+ /**
+ * Positive infinity of type half-precision float.
+ *
+ * @hide
+ */
+ public static final short POSITIVE_INFINITY = (short) 0x7c00;
+ /**
+ * Positive 0 of type half-precision float.
+ *
+ * @hide
+ */
+ public static final short POSITIVE_ZERO = (short) 0x0000;
+
+ /**
+ * The offset to shift by to obtain the sign bit.
+ *
+ * @hide
+ */
+ public static final int SIGN_SHIFT = 15;
+
+ /**
+ * The offset to shift by to obtain the exponent bits.
+ *
+ * @hide
+ */
+ public static final int EXPONENT_SHIFT = 10;
+
+ /**
+ * The bitmask to AND a number with to obtain the sign bit.
+ *
+ * @hide
+ */
+ public static final int SIGN_MASK = 0x8000;
+
+ /**
+ * The bitmask to AND a number shifted by {@link #EXPONENT_SHIFT} right, to obtain exponent bits.
+ *
+ * @hide
+ */
+ public static final int SHIFTED_EXPONENT_MASK = 0x1f;
+
+ /**
+ * The bitmask to AND a number with to obtain significand bits.
+ *
+ * @hide
+ */
+ public static final int SIGNIFICAND_MASK = 0x3ff;
+
+ /**
+ * The bitmask to AND with to obtain exponent and significand bits.
+ *
+ * @hide
+ */
+ public static final int EXPONENT_SIGNIFICAND_MASK = 0x7fff;
+
+ /**
+ * The offset of the exponent from the actual value.
+ *
+ * @hide
+ */
+ public static final int EXPONENT_BIAS = 15;
+
+ private static final int FP32_SIGN_SHIFT = 31;
+ private static final int FP32_EXPONENT_SHIFT = 23;
+ private static final int FP32_SHIFTED_EXPONENT_MASK = 0xff;
+ private static final int FP32_SIGNIFICAND_MASK = 0x7fffff;
+ private static final int FP32_EXPONENT_BIAS = 127;
+ private static final int FP32_QNAN_MASK = 0x400000;
+ private static final int FP32_DENORMAL_MAGIC = 126 << 23;
+ private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
+
+ /** Hidden constructor to prevent instantiation. */
+ private FP16() {}
+
+ /**
+ * <p>Compares the two specified half-precision float values. The following
+ * conditions apply during the comparison:</p>
+ *
+ * <ul>
+ * <li>{@link #NaN} is considered by this method to be equal to itself and greater
+ * than all other half-precision float values (including {@code #POSITIVE_INFINITY})</li>
+ * <li>{@link #POSITIVE_ZERO} is considered by this method to be greater than
+ * {@link #NEGATIVE_ZERO}.</li>
+ * </ul>
+ *
+ * @param x The first half-precision float value to compare.
+ * @param y The second half-precision float value to compare
+ *
+ * @return The value {@code 0} if {@code x} is numerically equal to {@code y}, a
+ * value less than {@code 0} if {@code x} is numerically less than {@code y},
+ * and a value greater than {@code 0} if {@code x} is numerically greater
+ * than {@code y}
+ *
+ * @hide
+ */
+ public static int compare(short x, short y) {
+ if (less(x, y)) return -1;
+ if (greater(x, y)) return 1;
+
+ // Collapse NaNs, akin to halfToIntBits(), but we want to keep
+ // (signed) short value types to preserve the ordering of -0.0
+ // and +0.0
+ short xBits = isNaN(x) ? NaN : x;
+ short yBits = isNaN(y) ? NaN : y;
+
+ return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1));
+ }
+
+ /**
+ * Returns the closest integral half-precision float value to the specified
+ * half-precision float value. Special values are handled in the
+ * following ways:
+ * <ul>
+ * <li>If the specified half-precision float is NaN, the result is NaN</li>
+ * <li>If the specified half-precision float is infinity (negative or positive),
+ * the result is infinity (with the same sign)</li>
+ * <li>If the specified half-precision float is zero (negative or positive),
+ * the result is zero (with the same sign)</li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return The value of the specified half-precision float rounded to the nearest
+ * half-precision float value
+ *
+ * @hide
+ */
+ public static short rint(short h) {
+ int bits = h & 0xffff;
+ int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+ int result = bits;
+
+ if (abs < 0x3c00) {
+ result &= SIGN_MASK;
+ if (abs > 0x3800){
+ result |= 0x3c00;
+ }
+ } else if (abs < 0x6400) {
+ int exp = 25 - (abs >> 10);
+ int mask = (1 << exp) - 1;
+ result += ((1 << (exp - 1)) - (~(abs >> exp) & 1));
+ result &= ~mask;
+ }
+ if (isNaN((short) result)) {
+ // if result is NaN mask with qNaN
+ // (i.e. mask the most significant mantissa bit with 1)
+ // to comply with hardware implementations (ARM64, Intel, etc).
+ result |= NaN;
+ }
+
+ return (short) result;
+ }
+
+ /**
+ * Returns the smallest half-precision float value toward negative infinity
+ * greater than or equal to the specified half-precision float value.
+ * Special values are handled in the following ways:
+ * <ul>
+ * <li>If the specified half-precision float is NaN, the result is NaN</li>
+ * <li>If the specified half-precision float is infinity (negative or positive),
+ * the result is infinity (with the same sign)</li>
+ * <li>If the specified half-precision float is zero (negative or positive),
+ * the result is zero (with the same sign)</li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return The smallest half-precision float value toward negative infinity
+ * greater than or equal to the specified half-precision float value
+ *
+ * @hide
+ */
+ public static short ceil(short h) {
+ int bits = h & 0xffff;
+ int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+ int result = bits;
+
+ if (abs < 0x3c00) {
+ result &= SIGN_MASK;
+ result |= 0x3c00 & -(~(bits >> 15) & (abs != 0 ? 1 : 0));
+ } else if (abs < 0x6400) {
+ abs = 25 - (abs >> 10);
+ int mask = (1 << abs) - 1;
+ result += mask & ((bits >> 15) - 1);
+ result &= ~mask;
+ }
+ if (isNaN((short) result)) {
+ // if result is NaN mask with qNaN
+ // (i.e. mask the most significant mantissa bit with 1)
+ // to comply with hardware implementations (ARM64, Intel, etc).
+ result |= NaN;
+ }
+
+ return (short) result;
+ }
+
+ /**
+ * Returns the largest half-precision float value toward positive infinity
+ * less than or equal to the specified half-precision float value.
+ * Special values are handled in the following ways:
+ * <ul>
+ * <li>If the specified half-precision float is NaN, the result is NaN</li>
+ * <li>If the specified half-precision float is infinity (negative or positive),
+ * the result is infinity (with the same sign)</li>
+ * <li>If the specified half-precision float is zero (negative or positive),
+ * the result is zero (with the same sign)</li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return The largest half-precision float value toward positive infinity
+ * less than or equal to the specified half-precision float value
+ *
+ * @hide
+ */
+ public static short floor(short h) {
+ int bits = h & 0xffff;
+ int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+ int result = bits;
+
+ if (abs < 0x3c00) {
+ result &= SIGN_MASK;
+ result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0);
+ } else if (abs < 0x6400) {
+ abs = 25 - (abs >> 10);
+ int mask = (1 << abs) - 1;
+ result += mask & -(bits >> 15);
+ result &= ~mask;
+ }
+ if (isNaN((short) result)) {
+ // if result is NaN mask with qNaN
+ // i.e. (Mask the most significant mantissa bit with 1)
+ result |= NaN;
+ }
+
+ return (short) result;
+ }
+
+ /**
+ * Returns the truncated half-precision float value of the specified
+ * half-precision float value. Special values are handled in the following ways:
+ * <ul>
+ * <li>If the specified half-precision float is NaN, the result is NaN</li>
+ * <li>If the specified half-precision float is infinity (negative or positive),
+ * the result is infinity (with the same sign)</li>
+ * <li>If the specified half-precision float is zero (negative or positive),
+ * the result is zero (with the same sign)</li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return The truncated half-precision float value of the specified
+ * half-precision float value
+ *
+ * @hide
+ */
+ public static short trunc(short h) {
+ int bits = h & 0xffff;
+ int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+ int result = bits;
+
+ if (abs < 0x3c00) {
+ result &= SIGN_MASK;
+ } else if (abs < 0x6400) {
+ abs = 25 - (abs >> 10);
+ int mask = (1 << abs) - 1;
+ result &= ~mask;
+ }
+
+ return (short) result;
+ }
+
+ /**
+ * Returns the smaller of two half-precision float values (the value closest
+ * to negative infinity). Special values are handled in the following ways:
+ * <ul>
+ * <li>If either value is NaN, the result is NaN</li>
+ * <li>{@link #NEGATIVE_ZERO} is smaller than {@link #POSITIVE_ZERO}</li>
+ * </ul>
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ * @return The smaller of the two specified half-precision values
+ *
+ * @hide
+ */
+ public static short min(short x, short y) {
+ if (isNaN(x)) return NaN;
+ if (isNaN(y)) return NaN;
+
+ if ((x & EXPONENT_SIGNIFICAND_MASK) == 0 && (y & EXPONENT_SIGNIFICAND_MASK) == 0) {
+ return (x & SIGN_MASK) != 0 ? x : y;
+ }
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+ }
+
+ /**
+ * Returns the larger of two half-precision float values (the value closest
+ * to positive infinity). Special values are handled in the following ways:
+ * <ul>
+ * <li>If either value is NaN, the result is NaN</li>
+ * <li>{@link #POSITIVE_ZERO} is greater than {@link #NEGATIVE_ZERO}</li>
+ * </ul>
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return The larger of the two specified half-precision values
+ *
+ * @hide
+ */
+ public static short max(short x, short y) {
+ if (isNaN(x)) return NaN;
+ if (isNaN(y)) return NaN;
+
+ if ((x & EXPONENT_SIGNIFICAND_MASK) == 0 && (y & EXPONENT_SIGNIFICAND_MASK) == 0) {
+ return (x & SIGN_MASK) != 0 ? y : x;
+ }
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+ }
+
+ /**
+ * Returns true if the first half-precision float value is less (smaller
+ * toward negative infinity) than the second half-precision float value.
+ * If either of the values is NaN, the result is false.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is less than y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean less(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ }
+
+ /**
+ * Returns true if the first half-precision float value is less (smaller
+ * toward negative infinity) than or equal to the second half-precision
+ * float value. If either of the values is NaN, the result is false.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is less than or equal to y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean lessEquals(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <=
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ }
+
+ /**
+ * Returns true if the first half-precision float value is greater (larger
+ * toward positive infinity) than the second half-precision float value.
+ * If either of the values is NaN, the result is false.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is greater than y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean greater(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ }
+
+ /**
+ * Returns true if the first half-precision float value is greater (larger
+ * toward positive infinity) than or equal to the second half-precision float
+ * value. If either of the values is NaN, the result is false.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is greater than y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean greaterEquals(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >=
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ }
+
+ /**
+ * Returns true if the two half-precision float values are equal.
+ * If either of the values is NaN, the result is false. {@link #POSITIVE_ZERO}
+ * and {@link #NEGATIVE_ZERO} are considered equal.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is equal to y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean equals(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return x == y || ((x | y) & EXPONENT_SIGNIFICAND_MASK) == 0;
+ }
+
+ /**
+ * Returns true if the specified half-precision float value represents
+ * infinity, false otherwise.
+ *
+ * @param h A half-precision float value
+ * @return True if the value is positive infinity or negative infinity,
+ * false otherwise
+ *
+ * @hide
+ */
+ public static boolean isInfinite(short h) {
+ return (h & EXPONENT_SIGNIFICAND_MASK) == POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns true if the specified half-precision float value represents
+ * a Not-a-Number, false otherwise.
+ *
+ * @param h A half-precision float value
+ * @return True if the value is a NaN, false otherwise
+ *
+ * @hide
+ */
+ public static boolean isNaN(short h) {
+ return (h & EXPONENT_SIGNIFICAND_MASK) > POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns true if the specified half-precision float value is normalized
+ * (does not have a subnormal representation). If the specified value is
+ * {@link #POSITIVE_INFINITY}, {@link #NEGATIVE_INFINITY},
+ * {@link #POSITIVE_ZERO}, {@link #NEGATIVE_ZERO}, NaN or any subnormal
+ * number, this method returns false.
+ *
+ * @param h A half-precision float value
+ * @return True if the value is normalized, false otherwise
+ *
+ * @hide
+ */
+ public static boolean isNormalized(short h) {
+ return (h & POSITIVE_INFINITY) != 0 && (h & POSITIVE_INFINITY) != POSITIVE_INFINITY;
+ }
+
+ /**
+ * <p>Converts the specified half-precision float value into a
+ * single-precision float value. The following special cases are handled:</p>
+ * <ul>
+ * <li>If the input is {@link #NaN}, the returned value is {@link Float#NaN}</li>
+ * <li>If the input is {@link #POSITIVE_INFINITY} or
+ * {@link #NEGATIVE_INFINITY}, the returned value is respectively
+ * {@link Float#POSITIVE_INFINITY} or {@link Float#NEGATIVE_INFINITY}</li>
+ * <li>If the input is 0 (positive or negative), the returned value is +/-0.0f</li>
+ * <li>Otherwise, the returned value is a normalized single-precision float value</li>
+ * </ul>
+ *
+ * @param h The half-precision float value to convert to single-precision
+ * @return A normalized single-precision float value
+ *
+ * @hide
+ */
+ public static float toFloat(short h) {
+ int bits = h & 0xffff;
+ int s = bits & SIGN_MASK;
+ int e = (bits >>> EXPONENT_SHIFT) & SHIFTED_EXPONENT_MASK;
+ int m = (bits ) & SIGNIFICAND_MASK;
+
+ int outE = 0;
+ int outM = 0;
+
+ if (e == 0) { // Denormal or 0
+ if (m != 0) {
+ // Convert denorm fp16 into normalized fp32
+ float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
+ o -= FP32_DENORMAL_FLOAT;
+ return s == 0 ? o : -o;
+ }
+ } else {
+ outM = m << 13;
+ if (e == 0x1f) { // Infinite or NaN
+ outE = 0xff;
+ if (outM != 0) { // SNaNs are quieted
+ outM |= FP32_QNAN_MASK;
+ }
+ } else {
+ outE = e - EXPONENT_BIAS + FP32_EXPONENT_BIAS;
+ }
+ }
+
+ int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM;
+ return Float.intBitsToFloat(out);
+ }
+
+ /**
+ * <p>Converts the specified single-precision float value into a
+ * half-precision float value. The following special cases are handled:</p>
+ * <ul>
+ * <li>If the input is NaN (see {@link Float#isNaN(float)}), the returned
+ * value is {@link #NaN}</li>
+ * <li>If the input is {@link Float#POSITIVE_INFINITY} or
+ * {@link Float#NEGATIVE_INFINITY}, the returned value is respectively
+ * {@link #POSITIVE_INFINITY} or {@link #NEGATIVE_INFINITY}</li>
+ * <li>If the input is 0 (positive or negative), the returned value is
+ * {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
+ * <li>If the input is a less than {@link #MIN_VALUE}, the returned value
+ * is flushed to {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
+ * <li>If the input is a less than {@link #MIN_NORMAL}, the returned value
+ * is a denorm half-precision float</li>
+ * <li>Otherwise, the returned value is rounded to the nearest
+ * representable half-precision float value</li>
+ * </ul>
+ *
+ * @param f The single-precision float value to convert to half-precision
+ * @return A half-precision float value
+ *
+ * @hide
+ */
+ public static short toHalf(float f) {
+ int bits = Float.floatToRawIntBits(f);
+ int s = (bits >>> FP32_SIGN_SHIFT );
+ int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_SHIFTED_EXPONENT_MASK;
+ int m = (bits ) & FP32_SIGNIFICAND_MASK;
+
+ int outE = 0;
+ int outM = 0;
+
+ if (e == 0xff) { // Infinite or NaN
+ outE = 0x1f;
+ outM = m != 0 ? 0x200 : 0;
+ } else {
+ e = e - FP32_EXPONENT_BIAS + EXPONENT_BIAS;
+ if (e >= 0x1f) { // Overflow
+ outE = 0x1f;
+ } else if (e <= 0) { // Underflow
+ if (e < -10) {
+ // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
+ } else {
+ // The fp32 value is a normalized float less than MIN_NORMAL,
+ // we convert to a denorm fp16
+ m = m | 0x800000;
+ int shift = 14 - e;
+ outM = m >> shift;
+
+ int lowm = m & ((1 << shift) - 1);
+ int hway = 1 << (shift - 1);
+ // if above halfway or exactly halfway and outM is odd
+ if (lowm + (outM & 1) > hway){
+ // Round to nearest even
+ // Can overflow into exponent bit, which surprisingly is OK.
+ // This increment relies on the +outM in the return statement below
+ outM++;
+ }
+ }
+ } else {
+ outE = e;
+ outM = m >> 13;
+ // if above halfway or exactly halfway and outM is odd
+ if ((m & 0x1fff) + (outM & 0x1) > 0x1000) {
+ // Round to nearest even
+ // Can overflow into exponent bit, which surprisingly is OK.
+ // This increment relies on the +outM in the return statement below
+ outM++;
+ }
+ }
+ }
+ // The outM is added here as the +1 increments for outM above can
+ // cause an overflow in the exponent bit which is OK.
+ return (short) ((s << SIGN_SHIFT) | (outE << EXPONENT_SHIFT) + outM);
+ }
+
+ /**
+ * <p>Returns a hexadecimal string representation of the specified half-precision
+ * float value. If the value is a NaN, the result is <code>"NaN"</code>,
+ * otherwise the result follows this format:</p>
+ * <ul>
+ * <li>If the sign is positive, no sign character appears in the result</li>
+ * <li>If the sign is negative, the first character is <code>'-'</code></li>
+ * <li>If the value is inifinity, the string is <code>"Infinity"</code></li>
+ * <li>If the value is 0, the string is <code>"0x0.0p0"</code></li>
+ * <li>If the value has a normalized representation, the exponent and
+ * significand are represented in the string in two fields. The significand
+ * starts with <code>"0x1."</code> followed by its lowercase hexadecimal
+ * representation. Trailing zeroes are removed unless all digits are 0, then
+ * a single zero is used. The significand representation is followed by the
+ * exponent, represented by <code>"p"</code>, itself followed by a decimal
+ * string of the unbiased exponent</li>
+ * <li>If the value has a subnormal representation, the significand starts
+ * with <code>"0x0."</code> followed by its lowercase hexadecimal
+ * representation. Trailing zeroes are removed unless all digits are 0, then
+ * a single zero is used. The significand representation is followed by the
+ * exponent, represented by <code>"p-14"</code></li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return A hexadecimal string representation of the specified value
+ *
+ * @hide
+ */
+ public static String toHexString(short h) {
+ StringBuilder o = new StringBuilder();
+
+ int bits = h & 0xffff;
+ int s = (bits >>> SIGN_SHIFT );
+ int e = (bits >>> EXPONENT_SHIFT) & SHIFTED_EXPONENT_MASK;
+ int m = (bits ) & SIGNIFICAND_MASK;
+
+ if (e == 0x1f) { // Infinite or NaN
+ if (m == 0) {
+ if (s != 0) o.append('-');
+ o.append("Infinity");
+ } else {
+ o.append("NaN");
+ }
+ } else {
+ if (s == 1) o.append('-');
+ if (e == 0) {
+ if (m == 0) {
+ o.append("0x0.0p0");
+ } else {
+ o.append("0x0.");
+ String significand = Integer.toHexString(m);
+ o.append(significand.replaceFirst("0{2,}$", ""));
+ o.append("p-14");
+ }
+ } else {
+ o.append("0x1.");
+ String significand = Integer.toHexString(m);
+ o.append(significand.replaceFirst("0{2,}$", ""));
+ o.append('p');
+ o.append(Integer.toString(e - EXPONENT_BIAS));
+ }
+ }
+
+ return o.toString();
+ }
+}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index f5cb019f..c255be5 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -214,6 +214,19 @@
return throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
}
+static void Linux_setenv(JNIEnv* env, jobject, jstring javaName, jstring javaValue,
+ jboolean overwrite) {
+ ScopedRealUtf8Chars name(env, javaName);
+ if (name.c_str() == NULL) {
+ jniThrowNullPointerException(env);
+ }
+ ScopedRealUtf8Chars value(env, javaValue);
+ if (value.c_str() == NULL) {
+ jniThrowNullPointerException(env);
+ }
+ throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0));
+}
+
// ---- Registration ----
static const JNINativeMethod sMethods[] =
@@ -227,6 +240,7 @@
{ "lstat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_lstat },
{ "stat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_stat },
{ "nOpen", "(Ljava/lang/String;II)I", (void*)Linux_open },
+ { "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv },
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
diff --git a/ravenwood/scripts/remove-ravenizer-output.sh b/ravenwood/scripts/remove-ravenizer-output.sh
new file mode 100755
index 0000000..be15b71
--- /dev/null
+++ b/ravenwood/scripts/remove-ravenizer-output.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# 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.
+
+# Delete all the ravenizer output jar files from Soong's intermediate directory.
+
+# `-a -prune` is needed because otherwise find would be confused if the directory disappears.
+
+find "${ANDROID_BUILD_TOP:?}/out/soong/.intermediates/" \
+ -type d \
+ -name 'ravenizer' \
+ -print \
+ -exec rm -fr \{\} \; \
+ -a -prune
diff --git a/ravenwood/tests/coretest/Android.bp b/ravenwood/tests/coretest/Android.bp
new file mode 100644
index 0000000..d94475c
--- /dev/null
+++ b/ravenwood/tests/coretest/Android.bp
@@ -0,0 +1,25 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_ravenwood_test {
+ name: "RavenwoodCoreTest",
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "junit-params",
+ "platform-parametric-runner-lib",
+ "truth",
+ ],
+ srcs: [
+ "test/**/*.java",
+ ],
+ auto_gen_config: true,
+}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
new file mode 100644
index 0000000..6d8fb98
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
@@ -0,0 +1,356 @@
+/*
+ * 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.ravenwoodtest.runnercallbacktests;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.NoRavenizer;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+
+/**
+ * Tests to make sure {@link RavenwoodAwareTestRunner} produces expected callbacks in various
+ * error situations in places such as @BeforeClass / @AfterClass / Constructors, which are
+ * out of test method bodies.
+ */
+@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner.
+public class RavenwoodRunnerCallbackTest extends RavenwoodRunnerTestBase {
+
+ /**
+ * Throws an exception in @AfterClass. This should produce a critical error.
+ */
+ @RunWith(BlockJUnit4ClassRunner.class)
+ // CHECKSTYLE:OFF Generated code
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$AfterClassFailureTest
+ testStarted: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$AfterClassFailureTest)
+ testFinished: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$AfterClassFailureTest)
+ testStarted: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$AfterClassFailureTest)
+ testFinished: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$AfterClassFailureTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$AfterClassFailureTest
+ criticalError: Failures detected in @AfterClass, which would be swallowed by tradefed: FAILURE
+ testSuiteFinished: classes
+ testRunFinished: 2,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class AfterClassFailureTest {
+ public AfterClassFailureTest() {
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ throw new RuntimeException("FAILURE");
+ }
+
+ @Test
+ public void test1() {
+ }
+
+ @Test
+ public void test2() {
+ }
+
+ }
+
+ /**
+ * Assumption failure in @BeforeClass.
+ */
+ @RunWith(ParameterizedAndroidJunit4.class)
+ // Because the test uses ParameterizedAndroidJunit4 with two parameters,
+ // the whole class is executed twice.
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest
+ testSuiteStarted: [0]
+ testStarted: test1[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test1[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest)
+ testStarted: test2[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test2[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest)
+ testSuiteFinished: [0]
+ testSuiteStarted: [1]
+ testStarted: test1[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test1[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest)
+ testStarted: test2[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test2[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassAssumptionFailureTest)
+ testSuiteFinished: [1]
+ testSuiteFinished: classes
+ testRunFinished: 4,0,4,0
+ """)
+ // CHECKSTYLE:ON
+ public static class BeforeClassAssumptionFailureTest {
+ public BeforeClassAssumptionFailureTest(String param) {
+ }
+
+ @BeforeClass
+ public static void beforeClass() {
+ Assume.assumeTrue(false);
+ }
+
+ @Parameters
+ public static List<String> getParams() {
+ var params = new ArrayList<String>();
+ params.add("foo");
+ params.add("bar");
+ return params;
+ }
+
+ @Test
+ public void test1() {
+ }
+
+ @Test
+ public void test2() {
+ }
+ }
+
+ /**
+ * General exception in @BeforeClass.
+ */
+ @RunWith(ParameterizedAndroidJunit4.class)
+ // Because the test uses ParameterizedAndroidJunit4 with two parameters,
+ // the whole class is executed twice.
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest
+ testSuiteStarted: [0]
+ testStarted: test1[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest)
+ testFailure: FAILURE
+ testFinished: test1[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest)
+ testStarted: test2[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest)
+ testFailure: FAILURE
+ testFinished: test2[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest)
+ testSuiteFinished: [0]
+ testSuiteStarted: [1]
+ testStarted: test1[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest)
+ testFailure: FAILURE
+ testFinished: test1[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest)
+ testStarted: test2[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest)
+ testFailure: FAILURE
+ testFinished: test2[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BeforeClassExceptionTest)
+ testSuiteFinished: [1]
+ testSuiteFinished: classes
+ testRunFinished: 4,4,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class BeforeClassExceptionTest {
+ public BeforeClassExceptionTest(String param) {
+ }
+
+ @BeforeClass
+ public static void beforeClass() {
+ throw new RuntimeException("FAILURE");
+ }
+
+ @Parameters
+ public static List<String> getParams() {
+ var params = new ArrayList<String>();
+ params.add("foo");
+ params.add("bar");
+ return params;
+ }
+
+ @Test
+ public void test1() {
+ }
+
+ @Test
+ public void test2() {
+ }
+ }
+
+ /**
+ * Assumption failure from a @ClassRule.
+ */
+ @RunWith(ParameterizedAndroidJunit4.class)
+ // Because the test uses ParameterizedAndroidJunit4 with two parameters,
+ // the whole class is executed twice.
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest
+ testSuiteStarted: [0]
+ testStarted: test1[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test1[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest)
+ testStarted: test2[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test2[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest)
+ testSuiteFinished: [0]
+ testSuiteStarted: [1]
+ testStarted: test1[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test1[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest)
+ testStarted: test2[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test2[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleAssumptionFailureTest)
+ testSuiteFinished: [1]
+ testSuiteFinished: classes
+ testRunFinished: 4,0,4,0
+ """)
+ // CHECKSTYLE:ON
+ public static class ClassRuleAssumptionFailureTest {
+ @ClassRule
+ public static final TestRule sClassRule = new TestRule() {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ assumeTrue(false);
+ return null; // unreachable
+ }
+ };
+
+ public ClassRuleAssumptionFailureTest(String param) {
+ }
+
+ @Parameters
+ public static List<String> getParams() {
+ var params = new ArrayList<String>();
+ params.add("foo");
+ params.add("bar");
+ return params;
+ }
+
+ @Test
+ public void test1() {
+ }
+
+ @Test
+ public void test2() {
+ }
+ }
+
+ /**
+ * General exception from a @ClassRule.
+ */
+ @RunWith(ParameterizedAndroidJunit4.class)
+ // Because the test uses ParameterizedAndroidJunit4 with two parameters,
+ // the whole class is executed twice.
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest
+ testSuiteStarted: [0]
+ testStarted: test1[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test1[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest)
+ testStarted: test2[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test2[0](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest)
+ testSuiteFinished: [0]
+ testSuiteStarted: [1]
+ testStarted: test1[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test1[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest)
+ testStarted: test2[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest)
+ testAssumptionFailure: got: <false>, expected: is <true>
+ testFinished: test2[1](com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassRuleExceptionTest)
+ testSuiteFinished: [1]
+ testSuiteFinished: classes
+ testRunFinished: 4,0,4,0
+ """)
+ // CHECKSTYLE:ON
+ public static class ClassRuleExceptionTest {
+ @ClassRule
+ public static final TestRule sClassRule = new TestRule() {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ assumeTrue(false);
+ return null; // unreachable
+ }
+ };
+
+ public ClassRuleExceptionTest(String param) {
+ }
+
+ @Parameters
+ public static List<String> getParams() {
+ var params = new ArrayList<String>();
+ params.add("foo");
+ params.add("bar");
+ return params;
+ }
+
+ @Test
+ public void test1() {
+ }
+
+ @Test
+ public void test2() {
+ }
+ }
+
+ /**
+ * General exception from a @ClassRule.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
+ testFailure: Exception detected in constructor
+ testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class ExceptionFromInnerRunnerConstructorTest {
+ public ExceptionFromInnerRunnerConstructorTest(String arg1, String arg2) {
+ }
+
+ @Test
+ public void test1() {
+ }
+
+ @Test
+ public void test2() {
+ }
+ }
+}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
new file mode 100644
index 0000000..6ee443f
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
@@ -0,0 +1,452 @@
+/*
+ * 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.ravenwoodtest.runnercallbacktests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.NoRavenizer;
+import android.platform.test.ravenwood.RavenwoodConfig;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Test for @Config field extraction and validation.
+ */
+@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner.
+public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase {
+ public abstract static class ConfigInBaseClass {
+ static String PACKAGE_NAME = "com.ConfigInBaseClass";
+
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
+ .setPackageName(PACKAGE_NAME).build();
+ }
+
+ /**
+ * Make sure a config in the base class is detected.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest
+ testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest)
+ testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest
+ testSuiteFinished: classes
+ testRunFinished: 1,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class ConfigInBaseClassTest extends ConfigInBaseClass {
+ @Test
+ public void test() {
+ assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
+ .isEqualTo(PACKAGE_NAME);
+ }
+ }
+
+ /**
+ * Make sure a config in the base class is detected.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest
+ testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest)
+ testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest
+ testSuiteFinished: classes
+ testRunFinished: 1,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class ConfigOverridingTest extends ConfigInBaseClass {
+ static String PACKAGE_NAME_OVERRIDE = "com.ConfigOverridingTest";
+
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
+ .setPackageName(PACKAGE_NAME_OVERRIDE).build();
+
+ @Test
+ public void test() {
+ assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
+ .isEqualTo(PACKAGE_NAME_OVERRIDE);
+ }
+ }
+
+ /**
+ * Test to make sure that if a test has a config error, the failure would be reported from
+ * each test method.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: testMethod1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+ testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
+ testFinished: testMethod1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+ testStarted: testMethod2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+ testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
+ testFinished: testMethod2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+ testStarted: testMethod3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+ testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
+ testFinished: testMethod3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+ testSuiteFinished: classes
+ testRunFinished: 3,3,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class ErrorMustBeReportedFromEachTest {
+ @RavenwoodConfig.Config
+ private static RavenwoodConfig sConfig = // Invalid because it's private.
+ new RavenwoodConfig.Builder().build();
+
+ @Test
+ public void testMethod1() {
+ }
+
+ @Test
+ public void testMethod2() {
+ }
+
+ @Test
+ public void testMethod3() {
+ }
+ }
+
+ /**
+ * Invalid because there are two @Config's.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
+ testFailure: Class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.DuplicateConfigTest has multiple fields with @RavenwoodConfiguration.Config
+ testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class DuplicateConfigTest {
+
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig1 =
+ new RavenwoodConfig.Builder().build();
+
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig2 =
+ new RavenwoodConfig.Builder().build();
+
+ @Test
+ public void testConfig() {
+ }
+ }
+
+ /**
+ * @Config's must be static.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
+ testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest.sConfig expected to be public static
+ testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class NonStaticConfigTest {
+
+ @RavenwoodConfig.Config
+ public RavenwoodConfig sConfig =
+ new RavenwoodConfig.Builder().build();
+
+ @Test
+ public void testConfig() {
+ }
+ }
+
+ /**
+ * @Config's must be public.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
+ testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest.sConfig expected to be public static
+ testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class NonPublicConfigTest {
+
+ @RavenwoodConfig.Config
+ RavenwoodConfig sConfig =
+ new RavenwoodConfig.Builder().build();
+
+ @Test
+ public void testConfig() {
+ }
+ }
+
+ /**
+ * @Config's must be of type RavenwoodConfiguration.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
+ testFailure: Field com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.WrongTypeConfigTest.sConfig has @RavenwoodConfiguration.Config but type is not RavenwoodConfiguration
+ testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class WrongTypeConfigTest {
+
+ @RavenwoodConfig.Config
+ public Object sConfig =
+ new RavenwoodConfig.Builder().build();
+
+ @Test
+ public void testConfig() {
+ }
+
+ }
+
+ /**
+ * Config can't be used with a (instance) Rule.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
+ testFailure: RavenwoodConfiguration and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfiguration.
+ testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class WithInstanceRuleTest {
+
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig =
+ new RavenwoodConfig.Builder().build();
+
+ @Rule
+ public RavenwoodRule mRule = new RavenwoodRule.Builder().build();
+
+ @Test
+ public void testConfig() {
+ }
+ }
+
+ /**
+ * Config can't be used with a (static) Rule.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
+ testFailure: Exception detected in constructor
+ testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class WithStaticRuleTest {
+
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig =
+ new RavenwoodConfig.Builder().build();
+
+ @Rule
+ public static RavenwoodRule sRule = new RavenwoodRule.Builder().build();
+
+ @Test
+ public void testConfig() {
+ }
+ }
+
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest
+ testStarted: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest)
+ testFailure: Multiple nesting RavenwoodRule's are detected in the same class, which is not supported.
+ testFinished: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class DuplicateRulesTest {
+
+ @Rule
+ public final RavenwoodRule mRavenwood1 = new RavenwoodRule();
+
+ @Rule
+ public final RavenwoodRule mRavenwood2 = new RavenwoodRule();
+
+ @Test
+ public void testMultipleRulesNotAllowed() {
+ }
+ }
+
+ public static class RuleInBaseClass {
+ static String PACKAGE_NAME = "com.RuleInBaseClass";
+ @Rule
+ public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder()
+ .setPackageName(PACKAGE_NAME).build();
+ }
+
+ /**
+ * Make sure that RavenwoodRule in a base class takes effect.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest
+ testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest)
+ testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest
+ testSuiteFinished: classes
+ testRunFinished: 1,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class RuleInBaseClassSuccessTest extends RuleInBaseClass {
+
+ @Test
+ public void testRuleInBaseClass() {
+ assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
+ .isEqualTo(PACKAGE_NAME);
+ }
+ }
+
+ /**
+ * Make sure that having a config and a rule in a base class should fail.
+ * RavenwoodRule.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
+ testFailure: RavenwoodConfiguration and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfiguration.
+ testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class ConfigWithRuleInBaseClassTest extends RuleInBaseClass {
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build();
+
+ @Test
+ public void test() {
+ }
+ }
+
+ /**
+ * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}.
+ */
+ public abstract static class RuleWithDifferentTypeInBaseClass {
+ static String PACKAGE_NAME = "com.RuleWithDifferentTypeInBaseClass";
+ @Rule
+ public final TestRule mRavenwood1 = new RavenwoodRule.Builder()
+ .setPackageName(PACKAGE_NAME).build();
+ }
+
+ /**
+ * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
+ testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
+ testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
+ testSuiteFinished: classes
+ testRunFinished: 1,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass {
+
+ @Test
+ public void testRuleInBaseClass() {
+ assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
+ .isEqualTo(PACKAGE_NAME);
+ }
+ }
+
+ /**
+ * Make sure that having a config and a rule in a base class should fail, even if the field type is not
+ * RavenwoodRule.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest
+ testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest)
+ testFailure: RavenwoodConfiguration and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfiguration.
+ testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest
+ testSuiteFinished: classes
+ testRunFinished: 1,1,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class ConfigWithRuleWithDifferentTypeInBaseClassTest extends RuleWithDifferentTypeInBaseClass {
+ @RavenwoodConfig.Config
+ public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build();
+
+ @Test
+ public void test() {
+ }
+ }
+}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
new file mode 100644
index 0000000..9a6934bf
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
@@ -0,0 +1,220 @@
+/*
+ * 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.ravenwoodtest.runnercallbacktests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.NoRavenizer;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner;
+import android.util.Log;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Result;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.BiConsumer;
+
+
+/**
+ * Base class for tests to make sure {@link RavenwoodAwareTestRunner} produces expected callbacks
+ * in various situations. (most of them are error situations.)
+ *
+ * Subclasses must contain test classes as static inner classes with an {@link Expected} annotation.
+ * This class finds them using reflections and run them one by one directly using {@link JUnitCore},
+ * and check the callbacks.
+ *
+ * Subclasses do no need to have any test methods.
+ *
+ * The {@link Expected} annotation must contain the expected result as a string.
+ *
+ * This test abuses the fact that atest + tradefed + junit won't run nested classes automatically.
+ * (So atest won't show any results directly from the nested classes.)
+ *
+ * The actual test method is {@link #doTest}, which is executed for each target test class, using
+ * junit-params.
+ */
+@RunWith(JUnitParamsRunner.class)
+@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner.
+public abstract class RavenwoodRunnerTestBase {
+ private static final String TAG = "RavenwoodRunnerTestBase";
+
+ /**
+ * Annotation to specify the expected result for a class.
+ */
+ @Target({ElementType.TYPE})
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Expected {
+ String value();
+ }
+
+ /**
+ * Take a multiline string, strip all of them, remove empty lines, and return it.
+ */
+ private static String stripMultiLines(String resultString) {
+ var list = new ArrayList<String>();
+ for (var line : resultString.split("\n")) {
+ var s = line.strip();
+ if (s.length() > 0) {
+ list.add(s);
+ }
+ }
+ return String.join("\n", list);
+ }
+
+ /**
+ * Extract the expected result from @Expected.
+ */
+ private String getExpectedResult(Class<?> testClazz) {
+ var expect = testClazz.getAnnotation(Expected.class);
+ return stripMultiLines(expect.value());
+ }
+
+ /**
+ * List all the nested classrs with an {@link Expected} annotation in a given class.
+ */
+ public Class<?>[] getTestClasses() {
+ var thisClass = this.getClass();
+ var ret = Arrays.stream(thisClass.getNestMembers())
+ .filter((c) -> c.getAnnotation(Expected.class) != null)
+ .toArray(Class[]::new);
+
+ assertThat(ret.length).isGreaterThan(0);
+
+ return ret;
+ }
+
+ /**
+ * This is the actual test method. We use junit-params to run this method for each target
+ * test class, which are returned by {@link #getTestClasses}.
+ *
+ * It runs each test class, and compare the result collected with
+ * {@link ResultCollectingListener} to expected results (as strings).
+ */
+ @Test
+ @Parameters(method = "getTestClasses")
+ public void doTest(Class<?> testClazz) {
+ doTest(testClazz, getExpectedResult(testClazz));
+ }
+
+ /**
+ * Run a given test class, and compare the result collected with
+ * {@link ResultCollectingListener} to expected results (as a string).
+ */
+ private void doTest(Class<?> testClazz, String expectedResult) {
+ Log.i(TAG, "Running test for " + testClazz);
+ var junitCore = new JUnitCore();
+
+ // Create a listener.
+ var listener = new ResultCollectingListener();
+ junitCore.addListener(listener);
+
+ // Set a listener to critical errors. This will also prevent
+ // {@link RavenwoodAwareTestRunner} from calling System.exit() when there's
+ // a critical error.
+ RavenwoodAwareTestRunner.private$ravenwood().setCriticalErrorHandler(
+ listener.sCriticalErrorListener);
+
+ try {
+ // Run the test class.
+ junitCore.run(testClazz);
+ } finally {
+ // Clear the critical error listener.
+ RavenwoodAwareTestRunner.private$ravenwood().setCriticalErrorHandler(null);
+ }
+
+ // Check the result.
+ assertWithMessage("Failure in test class: " + testClazz.getCanonicalName() + "]")
+ .that(listener.getResult())
+ .isEqualTo(expectedResult);
+ }
+
+ /**
+ * A JUnit RunListener that collects all the callbacks as a single string.
+ */
+ private static class ResultCollectingListener extends RunListener {
+ private final ArrayList<String> mResult = new ArrayList<>();
+
+ public final BiConsumer<String, Throwable> sCriticalErrorListener = (message, th) -> {
+ mResult.add("criticalError: " + message + ": " + th.getMessage());
+ };
+
+ @Override
+ public void testRunStarted(Description description) throws Exception {
+ mResult.add("testRunStarted: " + description);
+ }
+
+ @Override
+ public void testRunFinished(Result result) throws Exception {
+ mResult.add("testRunFinished: "
+ + result.getRunCount() + ","
+ + result.getFailureCount() + ","
+ + result.getAssumptionFailureCount() + ","
+ + result.getIgnoreCount());
+ }
+
+ @Override
+ public void testSuiteStarted(Description description) throws Exception {
+ mResult.add("testSuiteStarted: " + description);
+ }
+
+ @Override
+ public void testSuiteFinished(Description description) throws Exception {
+ mResult.add("testSuiteFinished: " + description);
+ }
+
+ @Override
+ public void testStarted(Description description) throws Exception {
+ mResult.add("testStarted: " + description);
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ mResult.add("testFinished: " + description);
+ }
+
+ @Override
+ public void testFailure(Failure failure) throws Exception {
+ mResult.add("testFailure: " + failure.getException().getMessage());
+ }
+
+ @Override
+ public void testAssumptionFailure(Failure failure) {
+ mResult.add("testAssumptionFailure: " + failure.getException().getMessage());
+ }
+
+ @Override
+ public void testIgnored(Description description) throws Exception {
+ mResult.add("testIgnored: " + description);
+ }
+
+ public String getResult() {
+ return String.join("\n", mResult);
+ }
+ }
+}
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index d8366c5..9c86389 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -9,17 +9,13 @@
com.android.internal.logging.testing.UiEventLoggerFake
com.android.internal.os.AndroidPrintStream
com.android.internal.os.BatteryStatsHistory
-com.android.internal.os.BatteryStatsHistory$TraceDelegate
-com.android.internal.os.BatteryStatsHistory$VarintParceler
com.android.internal.os.BatteryStatsHistoryIterator
com.android.internal.os.Clock
com.android.internal.os.LongArrayMultiStateCounter
-com.android.internal.os.LongArrayMultiStateCounter$LongArrayContainer
com.android.internal.os.LongMultiStateCounter
com.android.internal.os.MonotonicClock
com.android.internal.os.PowerProfile
com.android.internal.os.PowerStats
-com.android.internal.os.PowerStats$Descriptor
com.android.internal.os.RuntimeInit
com.android.internal.power.EnergyConsumerStats
com.android.internal.power.ModemPowerProfile
@@ -46,6 +42,7 @@
android.util.EventLog
android.util.FloatProperty
android.util.FloatMath
+android.util.Half
android.util.IndentingPrintWriter
android.util.IntArray
android.util.IntProperty
@@ -122,15 +119,9 @@
android.os.BaseBundle
android.os.BatteryConsumer
android.os.BatteryStats
-android.os.BatteryStats$HistoryItem
-android.os.BatteryStats$HistoryStepDetails
-android.os.BatteryStats$HistoryTag
-android.os.BatteryStats$LongCounter
-android.os.BatteryStats$ProcessStateChange
android.os.BatteryUsageStats
android.os.BatteryUsageStatsQuery
android.os.Binder
-android.os.Binder$IdentitySupplier
android.os.BluetoothBatteryStats
android.os.Broadcaster
android.os.Build
@@ -141,7 +132,6 @@
android.os.DeadObjectException
android.os.DeadSystemException
android.os.FileUtils
-android.os.FileUtils$MemoryPipe
android.os.Handler
android.os.HandlerExecutor
android.os.HandlerThread
@@ -153,8 +143,6 @@
android.os.PackageTagsList
android.os.Parcel
android.os.ParcelFileDescriptor
-android.os.ParcelFileDescriptor$AutoCloseInputStream
-android.os.ParcelFileDescriptor$AutoCloseOutputStream
android.os.ParcelFormatException
android.os.ParcelUuid
android.os.Parcelable
@@ -168,7 +156,6 @@
android.os.RemoteException
android.os.ResultReceiver
android.os.ServiceManager
-android.os.ServiceManager$ServiceNotFoundException
android.os.ServiceSpecificException
android.os.StrictMode
android.os.SystemClock
@@ -179,18 +166,14 @@
android.os.Trace
android.os.TransactionTooLargeException
android.os.UserBatteryConsumer
-android.os.UserBatteryConsumer$Builder
android.os.UidBatteryConsumer
-android.os.UidBatteryConsumer$Builder
android.os.UserHandle
android.os.UserManager
android.os.VibrationAttributes
-android.os.VibrationAttributes$Builder
android.os.WakeLockStats
android.os.WorkSource
android.content.ClipData
-android.content.ClipData$Item
android.content.ClipDescription
android.content.ClipboardManager
android.content.ComponentName
@@ -207,11 +190,7 @@
android.content.pm.ComponentInfo
android.content.pm.PackageInfo
android.content.pm.PackageItemInfo
-android.content.pm.PackageManager$Flags
-android.content.pm.PackageManager$PackageInfoFlags
-android.content.pm.PackageManager$ApplicationInfoFlags
-android.content.pm.PackageManager$ComponentInfoFlags
-android.content.pm.PackageManager$ResolveInfoFlags
+android.content.pm.PackageManager
android.content.pm.PathPermission
android.content.pm.ProviderInfo
android.content.pm.ResolveInfo
@@ -222,7 +201,6 @@
android.content.res.ApkAssets
android.content.res.AssetFileDescriptor
android.content.res.AssetManager
-android.content.res.AssetManager$Builder
android.content.res.ColorStateList
android.content.res.ConfigurationBoundResourceCache
android.content.res.Configuration
@@ -236,7 +214,6 @@
android.content.res.FontScaleConverterImpl
android.content.res.FontScaleConverterFactory
android.content.res.Resources
-android.content.res.Resources$Theme
android.content.res.ResourceId
android.content.res.ResourcesImpl
android.content.res.ResourcesKey
@@ -259,7 +236,6 @@
android.database.DataSetObservable
android.database.DataSetObserver
android.database.MatrixCursor
-android.database.MatrixCursor$RowBuilder
android.database.MergeCursor
android.database.Observable
android.database.SQLException
@@ -267,7 +243,6 @@
android.database.sqlite.SQLiteException
android.text.TextUtils
-android.text.TextUtils$SimpleStringSplitter
android.accounts.Account
@@ -277,7 +252,11 @@
android.graphics.Insets
android.graphics.Interpolator
android.graphics.Matrix
+android.graphics.Matrix44
+android.graphics.Outline
+android.graphics.ParcelableColorSpace
android.graphics.Path
+android.graphics.PixelFormat
android.graphics.Point
android.graphics.PointF
android.graphics.Rect
@@ -293,14 +272,11 @@
android.app.Instrumentation
android.app.LocaleConfig
android.app.ResourcesManager
-android.app.ResourcesManager$UpdateHandler
android.app.WindowConfiguration
android.metrics.LogMaker
android.view.Display
-android.view.Display$HdrCapabilities
-android.view.Display$Mode
android.view.DisplayAdjustments
android.view.DisplayInfo
android.view.inputmethod.InputBinding
diff --git a/ravenwood/texts/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt
index f64f26d..3ec3e3c 100644
--- a/ravenwood/texts/ravenwood-standard-options.txt
+++ b/ravenwood/texts/ravenwood-standard-options.txt
@@ -6,8 +6,6 @@
--default-throw
# Uncomment below lines to enable each feature.
-# --enable-non-stub-method-check
---no-non-stub-method-check
#--default-method-call-hook
# com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -34,8 +32,11 @@
--substitute-annotation
android.ravenwood.annotation.RavenwoodReplace
---native-substitute-annotation
- android.ravenwood.annotation.RavenwoodNativeSubstitutionClass
+--redirect-annotation
+ android.ravenwood.annotation.RavenwoodRedirect
+
+--redirection-class-annotation
+ android.ravenwood.annotation.RavenwoodRedirectionClass
--class-load-hook-annotation
android.ravenwood.annotation.RavenwoodClassLoadHook
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt
index 3a7fab3..0dcd271 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt
@@ -17,7 +17,14 @@
package com.android.platform.test.ravenwood.ravenizer
+import com.android.hoststubgen.UserErrorException
+
/**
* Use it for internal exception that really shouldn't happen.
*/
class RavenizerInternalException(message: String) : Exception(message)
+
+/**
+ * Thrown when an invalid test is detected in the target jar. (e.g. JUni3 tests)
+ */
+class RavenizerInvalidTestException(message: String) : Exception(message), UserErrorException
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
index e92ef72..a38512e 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
@@ -44,6 +44,9 @@
/** Time took to build [ClasNodes] */
var loadStructureTime: Double = .0,
+ /** Time took to validate the classes */
+ var validationTime: Double = .0,
+
/** Total real time spent for converting the jar file */
var totalProcessTime: Double = .0,
@@ -67,6 +70,7 @@
RavenizerStats{
totalTime=$totalTime,
loadStructureTime=$loadStructureTime,
+ validationTime=$validationTime,
totalProcessTime=$totalProcessTime,
totalConversionTime=$totalConversionTime,
totalCopyTime=$totalCopyTime,
@@ -84,16 +88,44 @@
class Ravenizer(val options: RavenizerOptions) {
fun run() {
val stats = RavenizerStats()
+
+ val fatalValidation = options.fatalValidation.get
+
stats.totalTime = log.nTime {
- process(options.inJar.get, options.outJar.get, stats)
+ process(
+ options.inJar.get,
+ options.outJar.get,
+ options.enableValidation.get,
+ fatalValidation,
+ stats,
+ )
}
log.i(stats.toString())
}
- private fun process(inJar: String, outJar: String, stats: RavenizerStats) {
+ private fun process(
+ inJar: String,
+ outJar: String,
+ enableValidation: Boolean,
+ fatalValidation: Boolean,
+ stats: RavenizerStats,
+ ) {
var allClasses = ClassNodes.loadClassStructures(inJar) {
time -> stats.loadStructureTime = time
}
+ if (enableValidation) {
+ stats.validationTime = log.iTime("Validating classes") {
+ if (!validateClasses(allClasses)) {
+ var message = "Invalid test class(es) detected." +
+ " See error log for details."
+ if (fatalValidation) {
+ throw RavenizerInvalidTestException(message)
+ } else {
+ log.w("Warning: $message")
+ }
+ }
+ }
+ }
stats.totalProcessTime = log.iTime("$executableName processing $inJar") {
ZipFile(inJar).use { inZip ->
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
index e85e3be..e8341e5 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
@@ -27,6 +27,12 @@
/** Output jar file */
var outJar: SetOnce<String> = SetOnce(""),
+
+ /** Whether to enable test validation. */
+ var enableValidation: SetOnce<Boolean> = SetOnce(true),
+
+ /** Whether the validation failure is fatal or not. */
+ var fatalValidation: SetOnce<Boolean> = SetOnce(false),
) {
companion object {
fun parseArgs(args: Array<String>): RavenizerOptions {
@@ -52,6 +58,12 @@
"--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists()
"--out-jar" -> ret.outJar.set(nextArg())
+ "--enable-validation" -> ret.enableValidation.set(true)
+ "--disable-validation" -> ret.enableValidation.set(false)
+
+ "--fatal-validation" -> ret.fatalValidation.set(true)
+ "--no-fatal-validation" -> ret.fatalValidation.set(false)
+
else -> throw ArgumentsException("Unknown option: $arg")
}
} catch (e: SetOnce.SetMoreThanOnceException) {
@@ -74,6 +86,8 @@
RavenizerOptions{
inJar=$inJar,
outJar=$outJar,
+ enableValidation=$enableValidation,
+ fatalValidation=$fatalValidation,
}
""".trimIndent()
}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
index e026e7a..1aa70c08 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
@@ -15,10 +15,12 @@
*/
package com.android.platform.test.ravenwood.ravenizer
+import android.platform.test.annotations.NoRavenizer
import android.platform.test.ravenwood.RavenwoodAwareTestRunner
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.findAnyAnnotation
import com.android.hoststubgen.asm.startsWithAny
+import com.android.hoststubgen.asm.toHumanReadableClassName
import org.junit.rules.TestRule
import org.junit.runner.RunWith
import org.objectweb.asm.Type
@@ -30,6 +32,7 @@
val desc = type.descriptor
val descAsSet = setOf<String>(desc)
val internlName = type.internalName
+ val humanReadableName = type.internalName.toHumanReadableClassName()
}
val testAnotType = TypeHolder(org.junit.Test::class.java)
@@ -37,6 +40,7 @@
val classRuleAnotType = TypeHolder(org.junit.ClassRule::class.java)
val runWithAnotType = TypeHolder(RunWith::class.java)
val innerRunnerAnotType = TypeHolder(RavenwoodAwareTestRunner.InnerRunner::class.java)
+val noRavenizerAnotType = TypeHolder(NoRavenizer::class.java)
val testRuleType = TypeHolder(TestRule::class.java)
val ravenwoodTestRunnerType = TypeHolder(RavenwoodAwareTestRunner::class.java)
@@ -87,9 +91,12 @@
return this.startsWithAny(
"java/", // just in case...
"javax/",
+ "junit/",
"org/junit/",
"org/mockito/",
"kotlin/",
+ "androidx/",
+ "android/support/",
// TODO -- anything else?
)
}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
new file mode 100644
index 0000000..27092d2
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.platform.test.ravenwood.ravenizer
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.startsWithAny
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.log
+import org.objectweb.asm.tree.ClassNode
+
+fun validateClasses(classes: ClassNodes): Boolean {
+ var allOk = true
+ classes.forEach { allOk = checkClass(it, classes) && allOk }
+
+ return allOk
+}
+
+/**
+ * Validate a class.
+ *
+ * - A test class shouldn't extend
+ *
+ */
+fun checkClass(cn: ClassNode, classes: ClassNodes): Boolean {
+ if (cn.name.shouldByBypassed()) {
+ // Class doesn't need to be checked.
+ return true
+ }
+ var allOk = true
+
+ // See if there's any class that extends a legacy base class.
+ // But ignore the base classes in android.test.
+ if (!cn.name.startsWithAny("android/test/")) {
+ allOk = checkSuperClass(cn, cn, classes) && allOk
+ }
+ return allOk
+}
+
+fun checkSuperClass(targetClass: ClassNode, currentClass: ClassNode, classes: ClassNodes): Boolean {
+ if (currentClass.superName == null || currentClass.superName == "java/lang/Object") {
+ return true // No parent class
+ }
+ if (currentClass.superName.isLegacyTestBaseClass()) {
+ log.e("Error: Class ${targetClass.name.toHumanReadableClassName()} extends"
+ + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}.")
+ return false
+ }
+ classes.findClass(currentClass.superName)?.let {
+ return checkSuperClass(targetClass, it, classes)
+ }
+ // Super class not found.
+ // log.w("Class ${currentClass.superName} not found.")
+ return true
+}
+
+/**
+ * Check if a class internal name is a known legacy test base class.
+ */
+fun String.isLegacyTestBaseClass(): Boolean {
+ return this.startsWithAny(
+ "junit/framework/TestCase",
+
+ // In case the test doesn't statically include JUnit, we need
+ "android/test/AndroidTestCase",
+ "android/test/InstrumentationTestCase",
+ "android/test/InstrumentationTestSuite",
+ )
+}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
index 25cad02..bd9d96d 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
@@ -30,6 +30,7 @@
import com.android.platform.test.ravenwood.ravenizer.classRuleAnotType
import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass
import com.android.platform.test.ravenwood.ravenizer.innerRunnerAnotType
+import com.android.platform.test.ravenwood.ravenizer.noRavenizerAnotType
import com.android.platform.test.ravenwood.ravenizer.ravenwoodTestRunnerType
import com.android.platform.test.ravenwood.ravenizer.ruleAnotType
import com.android.platform.test.ravenwood.ravenizer.runWithAnotType
@@ -183,7 +184,7 @@
av.visit("value", ravenwoodTestRunnerType.type)
av.visitEnd()
}
- log.d("Processed ${classInternalName.toHumanReadableClassName()}")
+ log.i("Update the @RunWith: ${classInternalName.toHumanReadableClassName()}")
}
/*
@@ -301,7 +302,7 @@
override fun visitCode() {
visitFieldInsn(Opcodes.GETSTATIC,
ravenwoodTestRunnerType.internlName,
- RavenwoodAwareTestRunner.IMPLICIT_CLASS_MIN_RULE_NAME,
+ RavenwoodAwareTestRunner.IMPLICIT_CLASS_OUTER_RULE_NAME,
testRuleType.desc
)
visitFieldInsn(Opcodes.PUTSTATIC,
@@ -312,7 +313,7 @@
visitFieldInsn(Opcodes.GETSTATIC,
ravenwoodTestRunnerType.internlName,
- RavenwoodAwareTestRunner.IMPLICIT_CLASS_MAX_RULE_NAME,
+ RavenwoodAwareTestRunner.IMPLICIT_CLASS_INNER_RULE_NAME,
testRuleType.desc
)
visitFieldInsn(Opcodes.PUTSTATIC,
@@ -360,7 +361,7 @@
visitVarInsn(ALOAD, 0)
visitFieldInsn(Opcodes.GETSTATIC,
ravenwoodTestRunnerType.internlName,
- RavenwoodAwareTestRunner.IMPLICIT_INST_MIN_RULE_NAME,
+ RavenwoodAwareTestRunner.IMPLICIT_INST_OUTER_RULE_NAME,
testRuleType.desc
)
visitFieldInsn(Opcodes.PUTFIELD,
@@ -372,7 +373,7 @@
visitVarInsn(ALOAD, 0)
visitFieldInsn(Opcodes.GETSTATIC,
ravenwoodTestRunnerType.internlName,
- RavenwoodAwareTestRunner.IMPLICIT_INST_MAX_RULE_NAME,
+ RavenwoodAwareTestRunner.IMPLICIT_INST_INNER_RULE_NAME,
testRuleType.desc
)
visitFieldInsn(Opcodes.PUTFIELD,
@@ -435,7 +436,19 @@
companion object {
fun shouldProcess(classes: ClassNodes, className: String): Boolean {
- return isTestLookingClass(classes, className)
+ if (!isTestLookingClass(classes, className)) {
+ return false
+ }
+ // Don't process a class if it has a @NoRavenizer annotation.
+ classes.findClass(className)?.let { cn ->
+ if (cn.findAnyAnnotation(noRavenizerAnotType.descAsSet) != null) {
+ log.w("Class ${className.toHumanReadableClassName()} has" +
+ " @${noRavenizerAnotType.humanReadableName}. Skipping."
+ )
+ return false
+ }
+ }
+ return true
}
fun maybeApply(
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index 3d7ad0b..b97ff62 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -10,6 +10,7 @@
filegroup {
name: "services.accessibility-sources",
srcs: ["java/**/*.java"],
+ exclude_srcs: ["java/**/a11ychecker/*.java"],
path: "java",
visibility: ["//frameworks/base/services"],
}
@@ -26,16 +27,13 @@
},
srcs: [
":services.accessibility-sources",
- ":statslog-accessibility-java-gen",
"//frameworks/base/packages/SettingsLib/RestrictedLockUtils:SettingsLibRestrictedLockUtilsSrc",
],
libs: [
- "aatf",
"services.core",
"androidx.annotation_annotation",
],
static_libs: [
- "accessibility_protos_lite",
"com_android_server_accessibility_flags_lib",
"//frameworks/base/packages/SystemUI/aconfig:com_android_systemui_flags_lib",
],
@@ -70,12 +68,3 @@
name: "com_android_server_accessibility_flags_lib",
aconfig_declarations: "com_android_server_accessibility_flags",
}
-
-genrule {
- name: "statslog-accessibility-java-gen",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --java $(out) --module accessibility" +
- " --javaPackage com.android.server.accessibility.a11ychecker" +
- " --javaClass AccessibilityCheckerStatsLog --minApiLevel 34",
- out: ["java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsLog.java"],
-}
diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING
index 3f85a90..0bc25e2 100644
--- a/services/accessibility/TEST_MAPPING
+++ b/services/accessibility/TEST_MAPPING
@@ -1,34 +1,19 @@
{
"presubmit": [
{
- "name": "CtsAccessibilityServiceTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
+ "name": "CtsAccessibilityServiceTestCases"
},
{
- "name": "CtsAccessibilityTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
+ "name": "CtsAccessibilityTestCases"
},
{
- "name": "CtsUiAutomationTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
+ "name": "CtsUiAutomationTestCases"
},
{
- "name": "FrameworksServicesTests_accessibility_Presubmit"
+ "name": "FrameworksServicesTests_accessibility"
},
{
- "name": "FrameworksCoreTests_accessibility_NO_FLAKES"
+ "name": "FrameworksCoreTests_accessibility"
}
],
"postsubmit": [
@@ -45,12 +30,7 @@
"name": "CtsUiAutomationTestCases"
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.accessibility"
- }
- ]
+ "name": "FrameworksServicesTests_accessibility"
},
{
"name": "FrameworksCoreTests_accessibility"
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 08cc9c3..ee3bbca 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -117,7 +117,7 @@
name: "enable_magnification_follows_mouse"
namespace: "accessibility"
description: "Whether to enable mouse following for fullscreen magnification"
- bug: "335494097"
+ bug: "354696546"
}
flag {
@@ -216,6 +216,16 @@
}
flag {
+ name: "reset_input_dispatcher_before_first_touch_exploration"
+ namespace: "accessibility"
+ description: "Resets InputDispatcher state by sending ACTION_CANCEL before the first TouchExploration hover events"
+ bug: "364408887"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "scan_packages_without_lock"
namespace: "accessibility"
description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
@@ -228,6 +238,7 @@
description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
bug: "295575684"
}
+
flag {
name: "send_hover_events_based_on_event_stream"
namespace: "accessibility"
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index d16a665..1b2447e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -26,6 +26,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
+import android.hardware.input.InputManager;
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -57,6 +58,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.StringJoiner;
/**
@@ -748,6 +750,7 @@
if ((mEnabledFeatures & FLAG_FEATURE_MOUSE_KEYS) != 0) {
mMouseKeysInterceptor = new MouseKeysInterceptor(mAms,
+ Objects.requireNonNull(mContext.getSystemService(InputManager.class)),
Looper.myLooper(),
Display.DEFAULT_DISPLAY);
addFirstEventHandler(Display.DEFAULT_DISPLAY, mMouseKeysInterceptor);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7cbb97e..7580b69 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -58,6 +58,7 @@
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
+import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE;
import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityShortcutActivated;
import static com.android.internal.accessibility.util.AccessibilityUtils.isUserSetupCompleted;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
@@ -163,6 +164,7 @@
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import android.view.inputmethod.EditorInfo;
import com.android.internal.R;
@@ -366,6 +368,11 @@
private final List<SendWindowStateChangedEventRunnable> mSendWindowStateChangedEventRunnables =
new ArrayList<>();
+ @VisibleForTesting
+ final HashSet<IUserInitializationCompleteCallback>
+ mUserInitializationCompleteCallbacks =
+ new HashSet<IUserInitializationCompleteCallback>();
+
@GuardedBy("mLock")
private @UserIdInt int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -1610,7 +1617,7 @@
/**
* Invoked remotely over AIDL by SysUi when the accessibility button within the system's
- * navigation area has been clicked.
+ * navigation area has been clicked, or a gesture shortcut input has been performed.
*
* @param displayId The logical display id.
* @param targetName The flattened {@link ComponentName} string or the class name of a system
@@ -1640,7 +1647,26 @@
}
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::performAccessibilityShortcutInternal, this,
- displayId, SOFTWARE, targetName));
+ displayId, getShortcutTypeForGenericShortcutCalls(currentUserId), targetName));
+ }
+
+ /**
+ * AIDL-exposed method to show the dialog
+ * for choosing the target for the gesture or button shortcuts.
+ * The shortcut is determined by the current navigation mode.
+ *
+ * @param displayId The id for the display to show the dialog on.
+ */
+ @Override
+ @EnforcePermission(STATUS_BAR_SERVICE)
+ public void notifyAccessibilityButtonLongClicked(int displayId) {
+ notifyAccessibilityButtonLongClicked_enforcePermission();
+ int userId;
+ synchronized (mLock) {
+ userId = mCurrentUserId;
+ }
+ showAccessibilityTargetsSelection(displayId,
+ getShortcutTypeForGenericShortcutCalls(userId), userId);
}
/**
@@ -2034,6 +2060,17 @@
obtainMessage(AccessibilityManagerService::announceNewUserIfNeeded, this),
WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS);
}
+
+ for (IUserInitializationCompleteCallback callback
+ : mUserInitializationCompleteCallbacks) {
+ try {
+ callback.onUserInitializationComplete(mCurrentUserId);
+ } catch (RemoteException re) {
+ Log.e("AccessibilityManagerService",
+ "Error while dispatching userInitializationComplete callback: ",
+ re);
+ }
+ }
}
}
@@ -2327,16 +2364,18 @@
}
}
- private void showAccessibilityTargetsSelection(int displayId,
- @UserShortcutType int shortcutType) {
+ private void showAccessibilityTargetsSelection(int displayId, int shortcutType,
+ int userId) {
final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
final String chooserClassName = (shortcutType == HARDWARE)
? AccessibilityShortcutChooserActivity.class.getName()
: AccessibilityButtonChooserActivity.class.getName();
intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.putExtra(EXTRA_TYPE_TO_CHOOSE, shortcutType);
final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
- mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
+ mMainHandler.post(() ->
+ mContext.startActivityAsUser(intent, bundle, UserHandle.of(userId)));
}
private void launchShortcutTargetActivity(int displayId, ComponentName name) {
@@ -3994,7 +4033,7 @@
* Perform the accessibility shortcut action.
*
* @param shortcutType The shortcut type.
- * @param displayId The display id of the accessibility button.
+ * @param displayId The display id the shortcut is being performed from.
* @param targetName The flattened {@link ComponentName} string or the class name of a system
* class implementing a supported accessibility feature, or {@code null} if there's no
* specified target.
@@ -4014,7 +4053,8 @@
if (targetName == null) {
// In case there are many targets assigned to the given shortcut.
if (shortcutTargets.size() > 1) {
- showAccessibilityTargetsSelection(displayId, shortcutType);
+ showAccessibilityTargetsSelection(
+ displayId, shortcutType, getCurrentUserState().mUserId);
return;
}
targetName = shortcutTargets.get(0);
@@ -4886,40 +4926,26 @@
}
@Override
- @RequiresNoPermission
- public boolean startFlashNotificationSequence(String opPkg,
- @FlashNotificationReason int reason, IBinder token) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mFlashNotificationsController.startFlashNotificationSequence(opPkg,
- reason, token);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ @EnforcePermission(MANAGE_ACCESSIBILITY)
+ public boolean startFlashNotificationSequence(String opPkg, @FlashNotificationReason int reason,
+ IBinder token) {
+ startFlashNotificationSequence_enforcePermission();
+ return mFlashNotificationsController.startFlashNotificationSequence(opPkg, reason, token);
}
@Override
- @RequiresNoPermission
+ @EnforcePermission(MANAGE_ACCESSIBILITY)
public boolean stopFlashNotificationSequence(String opPkg) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mFlashNotificationsController.stopFlashNotificationSequence(opPkg);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ stopFlashNotificationSequence_enforcePermission();
+ return mFlashNotificationsController.stopFlashNotificationSequence(opPkg);
}
@Override
- @RequiresNoPermission
- public boolean startFlashNotificationEvent(String opPkg,
- @FlashNotificationReason int reason, String reasonPkg) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mFlashNotificationsController.startFlashNotificationEvent(opPkg,
- reason, reasonPkg);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ @EnforcePermission(MANAGE_ACCESSIBILITY)
+ public boolean startFlashNotificationEvent(String opPkg, @FlashNotificationReason int reason,
+ String reasonPkg) {
+ startFlashNotificationEvent_enforcePermission();
+ return mFlashNotificationsController.startFlashNotificationEvent(opPkg, reason, reasonPkg);
}
@Override
@@ -6251,6 +6277,24 @@
}
@Override
+ @RequiresNoPermission
+ public void registerUserInitializationCompleteCallback(
+ IUserInitializationCompleteCallback callback) {
+ synchronized (mLock) {
+ mUserInitializationCompleteCallbacks.add(callback);
+ }
+ }
+
+ @Override
+ @RequiresNoPermission
+ public void unregisterUserInitializationCompleteCallback(
+ IUserInitializationCompleteCallback callback) {
+ synchronized (mLock) {
+ mUserInitializationCompleteCallbacks.remove(callback);
+ }
+ }
+
+ @Override
@EnforcePermission(INJECT_EVENTS)
public void injectInputEventToInputFilter(InputEvent event) {
injectInputEventToInputFilter_enforcePermission();
@@ -6542,6 +6586,18 @@
callback));
}
+ @VisibleForTesting
+ int getShortcutTypeForGenericShortcutCalls(int userId) {
+ int navigationMode = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.NAVIGATION_MODE, -1, userId);
+ if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
+ return (navigationMode == NAV_BAR_MODE_GESTURAL) ? GESTURE : SOFTWARE;
+ } else {
+ return SOFTWARE;
+ }
+ }
+
void attachAccessibilityOverlayToDisplayInternal(
int interactionId,
int displayId,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index b18e6ba..0bf7ec00 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -57,6 +57,7 @@
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.util.ShortcutUtils;
import java.io.FileDescriptor;
@@ -707,11 +708,16 @@
}
/**
- * Returns true if navibar magnification or shortcut key magnification is enabled.
+ * Returns true if a magnification shortcut of any type is enabled.
*/
public boolean isShortcutMagnificationEnabledLocked() {
- return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME)
- || mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
+ for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
+ if (getShortcutTargetsInternalLocked(shortcutType)
+ .contains(MAGNIFICATION_CONTROLLER_NAME)) {
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
index 2ce5c2b..54368ca 100644
--- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
@@ -23,6 +23,7 @@
import android.annotation.RequiresPermission;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
+import android.hardware.input.InputManager;
import android.hardware.input.VirtualMouse;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseConfig;
@@ -34,8 +35,11 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.InputDevice;
import android.view.KeyEvent;
+import androidx.annotation.VisibleForTesting;
+
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
@@ -60,7 +64,7 @@
* mouse keys of each physical keyboard will control a single (global) mouse pointer.
*/
public class MouseKeysInterceptor extends BaseEventStreamTransformation
- implements Handler.Callback {
+ implements Handler.Callback, InputManager.InputDeviceListener {
private static final String LOG_TAG = "MouseKeysInterceptor";
// To enable these logs, run: 'adb shell setprop log.tag.MouseKeysInterceptor DEBUG'
@@ -77,10 +81,19 @@
private final AccessibilityManagerService mAms;
private final Handler mHandler;
+ private final InputManager mInputManager;
/** Thread to wait for virtual mouse creation to complete */
private final Thread mCreateVirtualMouseThread;
+ /**
+ * Map of device IDs to a map of key codes to their corresponding {@link MouseKeyEvent} values.
+ * To ensure thread safety for the map, all access and modification of the map
+ * should happen on the same thread, i.e., on the handler thread.
+ */
+ private final SparseArray<SparseArray<MouseKeyEvent>> mDeviceKeyCodeMap =
+ new SparseArray<>();
+
VirtualDeviceManager.VirtualDevice mVirtualDevice = null;
private VirtualMouse mVirtualMouse = null;
@@ -102,6 +115,21 @@
/** Whether scroll toggle is on */
private boolean mScrollToggleOn = false;
+ /** The ID of the input device that is currently active */
+ private int mActiveInputDeviceId = 0;
+
+ /**
+ * Enum representing different types of mouse key events, each associated with a specific
+ * key code.
+ *
+ * <p> These events correspond to various mouse actions such as directional movements,
+ * clicks, and scrolls, mapped to specific keys on the keyboard.
+ * The key codes here are the QWERTY key codes, and should be accessed via
+ * {@link MouseKeyEvent#getKeyCode(InputDevice)}
+ * so that it is mapped to the equivalent key on the keyboard layout of the keyboard device
+ * that is actually in use.
+ * </p>
+ */
public enum MouseKeyEvent {
DIAGONAL_UP_LEFT_MOVE(KeyEvent.KEYCODE_7),
UP_MOVE_OR_SCROLL(KeyEvent.KEYCODE_8),
@@ -117,34 +145,64 @@
RELEASE(KeyEvent.KEYCODE_COMMA),
SCROLL_TOGGLE(KeyEvent.KEYCODE_PERIOD);
- private final int mKeyCode;
+ private final int mLocationKeyCode;
MouseKeyEvent(int enumValue) {
- mKeyCode = enumValue;
+ mLocationKeyCode = enumValue;
}
- private static final SparseArray<MouseKeyEvent> VALUE_TO_ENUM_MAP = new SparseArray<>();
-
- static {
- for (MouseKeyEvent type : MouseKeyEvent.values()) {
- VALUE_TO_ENUM_MAP.put(type.mKeyCode, type);
- }
- }
-
+ @VisibleForTesting
public final int getKeyCodeValue() {
- return mKeyCode;
+ return mLocationKeyCode;
}
/**
- * Convert int value of the key code to corresponding MouseEvent enum. If no matching
- * value is found, this will return {@code null}.
+ * Get the key code associated with the given MouseKeyEvent for the given keyboard
+ * input device, taking into account its layout.
+ * The default is to return the keycode for the default layout (QWERTY).
+ * We check if the input device has been generated using {@link InputDevice#getGeneration()}
+ * to test with the default {@link MouseKeyEvent} values in the unit tests.
+ */
+ public int getKeyCode(InputDevice inputDevice) {
+ if (inputDevice.getGeneration() == -1) {
+ return mLocationKeyCode;
+ }
+ return inputDevice.getKeyCodeForKeyLocation(mLocationKeyCode);
+ }
+
+ /**
+ * Convert int value of the key code to corresponding {@link MouseKeyEvent}
+ * enum for a particular device ID.
+ * If no matching value is found, this will return {@code null}.
*/
@Nullable
- public static MouseKeyEvent from(int value) {
- return VALUE_TO_ENUM_MAP.get(value);
+ public static MouseKeyEvent from(int keyCode, int deviceId,
+ SparseArray<SparseArray<MouseKeyEvent>> deviceKeyCodeMap) {
+ SparseArray<MouseKeyEvent> keyCodeToEnumMap = deviceKeyCodeMap.get(deviceId);
+ if (keyCodeToEnumMap != null) {
+ return keyCodeToEnumMap.get(keyCode);
+ }
+ return null;
}
}
/**
+ * Create a map of key codes to their corresponding {@link MouseKeyEvent} values
+ * for a specific input device.
+ * The key for {@code mDeviceKeyCodeMap} is the deviceId.
+ * The key for {@code keyCodeToEnumMap} is the keycode for each
+ * {@link MouseKeyEvent} according to the keyboard layout of the input device.
+ */
+ public void initializeDeviceToEnumMap(InputDevice inputDevice) {
+ int deviceId = inputDevice.getId();
+ SparseArray<MouseKeyEvent> keyCodeToEnumMap = new SparseArray<>();
+ for (MouseKeyEvent mouseKeyEventType : MouseKeyEvent.values()) {
+ int keyCode = mouseKeyEventType.getKeyCode(inputDevice);
+ keyCodeToEnumMap.put(keyCode, mouseKeyEventType);
+ }
+ mDeviceKeyCodeMap.put(deviceId, keyCodeToEnumMap);
+ }
+
+ /**
* Construct a new MouseKeysInterceptor.
*
* @param service The service to notify of key events
@@ -152,8 +210,10 @@
* @param displayId Display ID to send mouse events to
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
- public MouseKeysInterceptor(AccessibilityManagerService service, Looper looper, int displayId) {
+ public MouseKeysInterceptor(AccessibilityManagerService service,
+ InputManager inputManager, Looper looper, int displayId) {
mAms = service;
+ mInputManager = inputManager;
mHandler = new Handler(looper, this);
// Create the virtual mouse on a separate thread since virtual device creation
// should happen on an auxiliary thread, and not from the handler's thread.
@@ -163,6 +223,9 @@
mVirtualMouse = createVirtualMouse(displayId);
});
mCreateVirtualMouseThread.start();
+ // Register an input device listener to watch when input devices are
+ // added, removed or reconfigured.
+ mInputManager.registerInputDeviceListener(this, mHandler);
}
/**
@@ -215,7 +278,8 @@
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
private void performMouseScrollAction(int keyCode) {
- MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(keyCode);
+ MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
+ keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
float y = switch (mouseKeyEvent) {
case UP_MOVE_OR_SCROLL -> 1.0f;
case DOWN_MOVE_OR_SCROLL -> -1.0f;
@@ -247,15 +311,18 @@
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
private void performMouseButtonAction(int keyCode) {
- MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(keyCode);
+ MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
+ keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
int buttonCode = switch (mouseKeyEvent) {
case LEFT_CLICK -> VirtualMouseButtonEvent.BUTTON_PRIMARY;
case RIGHT_CLICK -> VirtualMouseButtonEvent.BUTTON_SECONDARY;
default -> VirtualMouseButtonEvent.BUTTON_UNKNOWN;
};
if (buttonCode != VirtualMouseButtonEvent.BUTTON_UNKNOWN) {
- sendVirtualMouseButtonEvent(buttonCode, VirtualMouseButtonEvent.ACTION_BUTTON_PRESS);
- sendVirtualMouseButtonEvent(buttonCode, VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE);
+ sendVirtualMouseButtonEvent(buttonCode,
+ VirtualMouseButtonEvent.ACTION_BUTTON_PRESS);
+ sendVirtualMouseButtonEvent(buttonCode,
+ VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE);
}
if (DEBUG) {
if (buttonCode == VirtualMouseButtonEvent.BUTTON_UNKNOWN) {
@@ -293,7 +360,9 @@
private void performMousePointerAction(int keyCode) {
float x = 0f;
float y = 0f;
- MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(keyCode);
+ MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
+ keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
+
switch (mouseKeyEvent) {
case DIAGONAL_DOWN_LEFT_MOVE -> {
x = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2);
@@ -339,18 +408,19 @@
}
}
- private boolean isMouseKey(int keyCode) {
- return MouseKeyEvent.VALUE_TO_ENUM_MAP.contains(keyCode);
+ private boolean isMouseKey(int keyCode, int deviceId) {
+ SparseArray<MouseKeyEvent> keyCodeToEnumMap = mDeviceKeyCodeMap.get(deviceId);
+ return keyCodeToEnumMap.contains(keyCode);
}
- private boolean isMouseButtonKey(int keyCode) {
- return keyCode == MouseKeyEvent.LEFT_CLICK.getKeyCodeValue()
- || keyCode == MouseKeyEvent.RIGHT_CLICK.getKeyCodeValue();
+ private boolean isMouseButtonKey(int keyCode, InputDevice inputDevice) {
+ return keyCode == MouseKeyEvent.LEFT_CLICK.getKeyCode(inputDevice)
+ || keyCode == MouseKeyEvent.RIGHT_CLICK.getKeyCode(inputDevice);
}
- private boolean isMouseScrollKey(int keyCode) {
- return keyCode == MouseKeyEvent.UP_MOVE_OR_SCROLL.getKeyCodeValue()
- || keyCode == MouseKeyEvent.DOWN_MOVE_OR_SCROLL.getKeyCodeValue();
+ private boolean isMouseScrollKey(int keyCode, InputDevice inputDevice) {
+ return keyCode == MouseKeyEvent.UP_MOVE_OR_SCROLL.getKeyCode(inputDevice)
+ || keyCode == MouseKeyEvent.DOWN_MOVE_OR_SCROLL.getKeyCode(inputDevice);
}
/**
@@ -373,7 +443,7 @@
}
/**
- * Handles key events and forwards mouse key events to the virtual mouse.
+ * Handles key events and forwards mouse key events to the virtual mouse on the handler thread.
*
* @param event The key event to handle.
* @param policyFlags The policy flags associated with the key event.
@@ -385,31 +455,45 @@
mAms.getTraceManager().logTrace(LOG_TAG + ".onKeyEvent",
FLAGS_INPUT_FILTER, "event=" + event + ";policyFlags=" + policyFlags);
}
+
+ mHandler.post(() -> {
+ onKeyEventInternal(event, policyFlags);
+ });
+ }
+
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ private void onKeyEventInternal(KeyEvent event, int policyFlags) {
boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
int keyCode = event.getKeyCode();
+ mActiveInputDeviceId = event.getDeviceId();
+ InputDevice inputDevice = mInputManager.getInputDevice(mActiveInputDeviceId);
- if (!isMouseKey(keyCode)) {
+ if (!mDeviceKeyCodeMap.contains(mActiveInputDeviceId)) {
+ initializeDeviceToEnumMap(inputDevice);
+ }
+
+ if (!isMouseKey(keyCode, mActiveInputDeviceId)) {
// Pass non-mouse key events to the next handler
super.onKeyEvent(event, policyFlags);
} else if (isDown) {
- if (keyCode == MouseKeyEvent.SCROLL_TOGGLE.getKeyCodeValue()) {
+ if (keyCode == MouseKeyEvent.SCROLL_TOGGLE.getKeyCode(inputDevice)) {
mScrollToggleOn = !mScrollToggleOn;
if (DEBUG) {
Slog.d(LOG_TAG, "Scroll toggle " + (mScrollToggleOn ? "ON" : "OFF"));
}
- } else if (keyCode == MouseKeyEvent.HOLD.getKeyCodeValue()) {
+ } else if (keyCode == MouseKeyEvent.HOLD.getKeyCode(inputDevice)) {
sendVirtualMouseButtonEvent(
VirtualMouseButtonEvent.BUTTON_PRIMARY,
VirtualMouseButtonEvent.ACTION_BUTTON_PRESS
);
- } else if (keyCode == MouseKeyEvent.RELEASE.getKeyCodeValue()) {
+ } else if (keyCode == MouseKeyEvent.RELEASE.getKeyCode(inputDevice)) {
sendVirtualMouseButtonEvent(
VirtualMouseButtonEvent.BUTTON_PRIMARY,
VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE
);
- } else if (isMouseButtonKey(keyCode)) {
+ } else if (isMouseButtonKey(keyCode, inputDevice)) {
performMouseButtonAction(keyCode);
- } else if (mScrollToggleOn && isMouseScrollKey(keyCode)) {
+ } else if (mScrollToggleOn && isMouseScrollKey(keyCode, inputDevice)) {
// If the scroll key is pressed down and no other key is active,
// set it as the active key and send a message to scroll the pointer
if (mActiveScrollKey == KEY_NOT_SET) {
@@ -439,7 +523,8 @@
mHandler.removeMessages(MESSAGE_SCROLL_MOUSE_POINTER);
} else {
Slog.i(LOG_TAG, "Dropping event with key code: '" + keyCode
- + "', with no matching down event from deviceId = " + event.getDeviceId());
+ + "', with no matching down event from deviceId = "
+ + event.getDeviceId());
}
}
}
@@ -503,12 +588,40 @@
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@Override
public void onDestroy() {
- // Clear mouse state
- mActiveMoveKey = KEY_NOT_SET;
- mActiveScrollKey = KEY_NOT_SET;
- mLastTimeKeyActionPerformed = 0;
+ mHandler.post(() -> {
+ // Clear mouse state
+ mActiveMoveKey = KEY_NOT_SET;
+ mActiveScrollKey = KEY_NOT_SET;
+ mLastTimeKeyActionPerformed = 0;
+ mDeviceKeyCodeMap.clear();
+ });
mHandler.removeCallbacksAndMessages(null);
mVirtualDevice.close();
}
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ mDeviceKeyCodeMap.remove(deviceId);
+ }
+
+ /**
+ * The user can change the keyboard layout from settings at anytime, which would change
+ * key character map for that device. Hence, we should use this callback to
+ * update the key code to enum mapping if there is a change in the physical keyboard detected.
+ *
+ * @param deviceId The id of the input device that changed.
+ */
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
+ // Update the enum mapping only if input device that changed is a keyboard
+ if (inputDevice.isFullKeyboard() && !mDeviceKeyCodeMap.contains(deviceId)) {
+ initializeDeviceToEnumMap(inputDevice);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java
index 83f57b2..950246f 100644
--- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java
@@ -87,7 +87,7 @@
public Set<AndroidAccessibilityCheckerResult> maybeRunA11yChecker(
List<AccessibilityNodeInfo> nodes, @Nullable String sourceEventClassName,
ComponentName a11yServiceComponentName, @UserIdInt int userId) {
- if (!shouldRunA11yChecker()) {
+ if (!shouldRunA11yChecker() || nodes.isEmpty()) {
return Set.of();
}
@@ -95,24 +95,33 @@
String defaultBrowserName = mPackageManager.getDefaultBrowserPackageNameAsUser(userId);
try {
+ AndroidAccessibilityCheckerResult.Builder commonResultBuilder =
+ AccessibilityCheckerUtils.getCommonResultBuilder(nodes.getFirst(),
+ sourceEventClassName, mPackageManager, a11yServiceComponentName);
+ if (commonResultBuilder == null) {
+ return Set.of();
+ }
for (AccessibilityNodeInfo nodeInfo : nodes) {
- // Skip browser results because they are mostly related to web content and not the
- // browser app itself.
+ // Skip browser results because they are mostly related to web content and
+ // not the browser app itself.
if (nodeInfo.getPackageName() == null
|| nodeInfo.getPackageName().toString().equals(defaultBrowserName)) {
continue;
}
- List<AccessibilityHierarchyCheckResult> checkResults = runChecksOnNode(nodeInfo);
+ List<AccessibilityHierarchyCheckResult> checkResults = runChecksOnNode(
+ nodeInfo);
Set<AndroidAccessibilityCheckerResult> filteredResults =
AccessibilityCheckerUtils.processResults(nodeInfo, checkResults,
- sourceEventClassName, mPackageManager, a11yServiceComponentName);
+ commonResultBuilder);
allResults.addAll(filteredResults);
}
mCachedResults.addAll(allResults);
+ return allResults;
+
} catch (RuntimeException e) {
Slog.e(LOG_TAG, "An unknown error occurred while running a11y checker.", e);
+ return Set.of();
}
- return allResults;
}
private List<AccessibilityHierarchyCheckResult> runChecksOnNode(
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
index eb24b027..a739304 100644
--- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
@@ -91,45 +91,55 @@
AccessibilityCheckClass.TRAVERSAL_ORDER_CHECK));
// LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/a11ychecker/proto/a11ychecker.proto)
- static Set<AndroidAccessibilityCheckerResult> processResults(
+
+ /**
+ * Returns AccessibilityCheckResultReported.Builder with the common fields for all nodes
+ * belonging in the same cache pre-filled.
+ */
+ static @Nullable AndroidAccessibilityCheckerResult.Builder getCommonResultBuilder(
AccessibilityNodeInfo nodeInfo,
- List<AccessibilityHierarchyCheckResult> checkResults,
@Nullable String activityClassName,
PackageManager packageManager,
ComponentName a11yServiceComponentName) {
- String appPackageName = nodeInfo.getPackageName().toString();
- String nodePath = AccessibilityNodePathBuilder.createNodePath(nodeInfo);
- if (nodePath == null) {
- return Set.of();
+ if (nodeInfo.getPackageName() == null) {
+ return null;
}
- AndroidAccessibilityCheckerResult.Builder commonBuilder;
+ String appPackageName = nodeInfo.getPackageName().toString();
try {
- commonBuilder = AndroidAccessibilityCheckerResult.newBuilder()
+ return AndroidAccessibilityCheckerResult.newBuilder()
.setPackageName(appPackageName)
.setAppVersionCode(getAppVersionCode(packageManager, appPackageName))
- .setUiElementPath(nodePath)
.setActivityName(
getActivityName(packageManager, appPackageName, activityClassName))
.setWindowTitle(getWindowTitle(nodeInfo))
.setSourceComponentName(a11yServiceComponentName)
- .setSourceVersionCode(
- getAppVersionCode(packageManager,
- a11yServiceComponentName.getPackageName()));
+ .setSourceVersionCode(getAppVersionCode(packageManager,
+ a11yServiceComponentName.getPackageName()));
} catch (PackageManager.NameNotFoundException e) {
Slog.e(LOG_TAG, "Unknown package name", e);
+ return null;
+ }
+ }
+
+ static Set<AndroidAccessibilityCheckerResult> processResults(
+ AccessibilityNodeInfo nodeInfo,
+ List<AccessibilityHierarchyCheckResult> checkResults,
+ AndroidAccessibilityCheckerResult.Builder resultBuilder) {
+ String nodePath = AccessibilityNodePathBuilder.createNodePath(nodeInfo);
+ if (resultBuilder == null || nodePath == null) {
return Set.of();
}
-
return checkResults.stream()
.filter(checkResult -> checkResult.getType()
== AccessibilityCheckResult.AccessibilityCheckResultType.ERROR
|| checkResult.getType()
== AccessibilityCheckResult.AccessibilityCheckResultType.WARNING)
- .map(checkResult -> new AndroidAccessibilityCheckerResult.Builder(
- commonBuilder).setResultCheckClass(
- getCheckClass(checkResult)).setResultType(
- getCheckResultType(checkResult)).setResultId(
- checkResult.getResultId()).build())
+ .map(checkResult -> new AndroidAccessibilityCheckerResult.Builder(resultBuilder)
+ .setUiElementPath(nodePath)
+ .setResultCheckClass(getCheckClass(checkResult))
+ .setResultType(getCheckResultType(checkResult))
+ .setResultId(checkResult.getResultId())
+ .build())
.collect(Collectors.toUnmodifiableSet());
}
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/Android.bp b/services/accessibility/java/com/android/server/accessibility/a11ychecker/Android.bp
new file mode 100644
index 0000000..e9ed202
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/Android.bp
@@ -0,0 +1,31 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// TODO(http://b/364326163): a11ychecker depends on aatf which currently can't be used in the system
+// server as it pulls in test deps. We moved a11ychecker sources from services.accessibility to an
+// isolated library while this is resolved.
+java_library_static {
+ name: "a11ychecker",
+ srcs: [
+ "*.java",
+ ":statslog-accessibility-java-gen",
+ ],
+ libs: [
+ "aatf",
+ "androidx.annotation_annotation",
+ ],
+ static_libs: [
+ "accessibility_protos_lite",
+ "com_android_server_accessibility_flags_lib",
+ ],
+}
+
+genrule {
+ name: "statslog-accessibility-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module accessibility" +
+ " --javaPackage com.android.server.accessibility.a11ychecker" +
+ " --javaClass AccessibilityCheckerStatsLog --minApiLevel 34",
+ out: ["java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsLog.java"],
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 04b42e4..0ed239e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -1613,6 +1613,19 @@
dispatchGesture(gestureEvent);
}
if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
+ if (Flags.resetInputDispatcherBeforeFirstTouchExploration()
+ && !mState.hasResetInputDispatcherState()) {
+ // Cancel any possible ongoing touch gesture from before touch exploration
+ // started. This clears out the InputDispatcher event stream state so that it
+ // is ready to accept new injected HOVER events.
+ mDispatcher.sendMotionEvent(
+ mEvents.get(0),
+ ACTION_CANCEL,
+ mRawEvents.get(0),
+ mPointerIdBits,
+ mPolicyFlags);
+ setHasResetInputDispatcherState(true);
+ }
// Deliver a down event.
mDispatcher.sendMotionEvent(
mEvents.get(0),
@@ -1773,4 +1786,9 @@
+ ", mDraggingPointerId: " + mDraggingPointerId
+ " }";
}
+
+ @VisibleForTesting
+ void setHasResetInputDispatcherState(boolean value) {
+ mState.setHasResetInputDispatcherState(value);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index e13994e..f15b8ee 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -86,6 +86,7 @@
private MotionEvent mLastInjectedHoverEvent;
// The last injected hover event used for performing clicks.
private MotionEvent mLastInjectedHoverEventForClick;
+ private boolean mHasResetInputDispatcherState;
// The time of the last injected down.
private long mLastInjectedDownEventTime;
// Keep track of which pointers sent to the system are down.
@@ -361,6 +362,14 @@
return mLastInjectedDownEventTime;
}
+ boolean hasResetInputDispatcherState() {
+ return mHasResetInputDispatcherState;
+ }
+
+ void setHasResetInputDispatcherState(boolean value) {
+ mHasResetInputDispatcherState = value;
+ }
+
public int getLastTouchedWindowId() {
return mLastTouchedWindowId;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
index 845249e..ab94e98 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
@@ -39,8 +39,14 @@
* @param displayId The display that is being magnified
*/
public void onEvent(MotionEvent event, int displayId) {
- if (event.getAction() == ACTION_HOVER_MOVE
- || (event.getAction() == ACTION_MOVE && event.getSource() == SOURCE_MOUSE)) {
+ // Ignore gesture events synthesized from the touchpad.
+ // TODO(b/354696546): Use synthesized pinch gestures to control scale.
+ boolean isSynthesizedFromTouchpad =
+ event.getClassification() != MotionEvent.CLASSIFICATION_NONE;
+
+ // Consume only move events from the mouse or hovers from any tool.
+ if (!isSynthesizedFromTouchpad && (event.getAction() == ACTION_HOVER_MOVE
+ || (event.getAction() == ACTION_MOVE && event.getSource() == SOURCE_MOUSE))) {
final float eventX = event.getX();
final float eventY = event.getY();
diff --git a/services/appfunctions/TEST_MAPPING b/services/appfunctions/TEST_MAPPING
new file mode 100644
index 0000000..91e82ec
--- /dev/null
+++ b/services/appfunctions/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksAppFunctionsTests"
+ },
+ {
+ "name": "CtsAppFunctionTestCases"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
index 954651d..02800cb 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
@@ -16,15 +16,12 @@
package com.android.server.appfunctions;
-import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
-
+import android.app.appfunctions.AppFunctionManagerConfiguration;
import android.content.Context;
import com.android.server.SystemService;
-/**
- * Service that manages app functions.
- */
+/** Service that manages app functions. */
public class AppFunctionManagerService extends SystemService {
private final AppFunctionManagerServiceImpl mServiceImpl;
@@ -35,7 +32,7 @@
@Override
public void onStart() {
- if (enableAppFunctionManager()) {
+ if (AppFunctionManagerConfiguration.isSupported(getContext())) {
publishBinderService(Context.APP_FUNCTION_SERVICE, mServiceImpl);
}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 53885fc..f04bd9f 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -25,6 +25,7 @@
import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback;
import android.content.Context;
import android.content.Intent;
+import android.os.Binder;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Slog;
@@ -38,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();
@@ -49,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;
}
@@ -89,125 +89,147 @@
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(new ExecuteAppFunctionResponse
- .Builder(ExecuteAppFunctionResponse.RESULT_DENIED,
- getExceptionMessage(exception)).build());
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_DENIED,
+ exception.getMessage(),
+ /* extras= */ null));
return;
}
// TODO(b/354956319): Add and honor the new enterprise policies.
if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
- safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse.Builder(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
- "Cannot run on a device with a device owner or from the managed profile."
- ).build());
+ 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(new ExecuteAppFunctionResponse.Builder(
- ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT,
- "Target package name cannot be empty."
- ).build());
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT,
+ "Target package name cannot be empty.",
+ /* extras= */ null));
return;
}
if (!mCallerValidator.verifyCallerCanExecuteAppFunction(
validatedCallingPackage, targetPackageName)) {
- safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse
- .Builder(ExecuteAppFunctionResponse.RESULT_DENIED,
- "Caller does not have permission to execute the appfunction")
- .build());
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_DENIED,
+ "Caller does not have permission to execute the appfunction",
+ /* extras= */ null));
return;
}
- Intent serviceIntent = mInternalServiceHelper.resolveAppFunctionService(
- targetPackageName,
- targetUser);
+ Intent serviceIntent =
+ mInternalServiceHelper.resolveAppFunctionService(targetPackageName, targetUser);
if (serviceIntent == null) {
- safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse.Builder(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
- "Cannot find the target service."
- ).build());
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+ "Cannot find the target service.",
+ /* extras= */ null));
return;
}
- bindAppFunctionServiceUnchecked(requestInternal, serviceIntent, targetUser,
- safeExecuteAppFunctionCallback,
- /*bindFlags=*/ Context.BIND_AUTO_CREATE,
- /*timeoutInMillis=*/ mServiceConfig.getExecuteAppFunctionTimeoutMillis());
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ bindAppFunctionServiceUnchecked(
+ requestInternal,
+ serviceIntent,
+ targetUser,
+ safeExecuteAppFunctionCallback,
+ /* bindFlags= */ Context.BIND_AUTO_CREATE,
+ /* timeoutInMillis= */ mServiceConfig.getExecuteAppFunctionTimeoutMillis());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
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(new ExecuteAppFunctionResponse
- .Builder(ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
- getExceptionMessage(e)).build());
- 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(new ExecuteAppFunctionResponse
- .Builder(ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
- "Failed to connect to AppFunctionService").build());
- }
+ @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(
- new ExecuteAppFunctionResponse.Builder(
- ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
- "Binding to AppFunctionService timed out."
- ).build());
- }
- }
- );
+ @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(new ExecuteAppFunctionResponse.Builder(
- ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
- "Failed to bind the AppFunctionService."
- ).build());
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
+ "Failed to bind the AppFunctionService.",
+ /* extras= */ null));
}
}
-
- private String getExceptionMessage(Exception exception) {
- return exception.getMessage() == null ? "" : exception.getMessage();
- }
}
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..618a5ae 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,21 +70,29 @@
@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) {
+ if (callerPackageName.equals(targetPackageName)) {
+ return true;
+ }
+
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 isSamePackage = callerPackageName.equals(targetPackageName);
- return hasExecutionPermission || hasTrustedExecutionPermission || isSamePackage;
+ boolean hasTrustedExecutionPermission =
+ mContext.checkPermission(
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ boolean hasExecutionPermission =
+ mContext.checkPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ return hasExecutionPermission || hasTrustedExecutionPermission;
}
@Override
@@ -111,13 +118,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 +144,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 +175,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/FutureAppSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
new file mode 100644
index 0000000..b1c25c4
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
@@ -0,0 +1,116 @@
+/*
+ * 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.server.appfunctions;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.GetByDocumentIdRequest;
+import android.app.appsearch.GetSchemaResponse;
+import android.app.appsearch.PutDocumentsRequest;
+import android.app.appsearch.RemoveByDocumentIdRequest;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+import android.app.appsearch.SearchSpec;
+import android.app.appsearch.SetSchemaRequest;
+import android.app.appsearch.SetSchemaResponse;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** A future API wrapper of {@link AppSearchSession} APIs. */
+public interface FutureAppSearchSession extends Closeable {
+
+ /** Converts a failed app search result codes into an exception. */
+ @NonNull
+ static 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());
+ default -> new IllegalStateException(appSearchResult.getErrorMessage());
+ };
+ }
+
+ /**
+ * Sets the schema that represents the organizational structure of data within the AppSearch
+ * database.
+ */
+ AndroidFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest setSchemaRequest);
+
+ /** Retrieves the schema most recently successfully provided to {@code setSchema}. */
+ AndroidFuture<GetSchemaResponse> getSchema();
+
+ /** Indexes documents into the {@link AppSearchSession} database. */
+ AndroidFuture<AppSearchBatchResult<String, Void>> put(
+ @NonNull PutDocumentsRequest putDocumentsRequest);
+
+ /** Removes {@link GenericDocument} from the index by Query. */
+ AndroidFuture<AppSearchBatchResult<String, Void>> remove(
+ @NonNull RemoveByDocumentIdRequest removeRequest);
+
+ /**
+ * Gets {@link GenericDocument} objects by document IDs in a namespace from the {@link
+ * AppSearchSession} database.
+ */
+ AndroidFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentId(
+ GetByDocumentIdRequest getRequest);
+
+ /**
+ * Retrieves documents from the open {@link AppSearchSession} that match a given query string
+ * and type of search provided.
+ */
+ AndroidFuture<FutureSearchResults> search(
+ @NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+
+ /** A future API wrapper of {@link android.app.appsearch.SearchResults}. */
+ class FutureSearchResults {
+ private final SearchResults mSearchResults;
+ private final Executor mExecutor;
+
+ public FutureSearchResults(
+ @NonNull SearchResults searchResults, @NonNull Executor executor) {
+ mSearchResults = Objects.requireNonNull(searchResults);
+ mExecutor = Objects.requireNonNull(executor);
+ }
+
+ public AndroidFuture<List<SearchResult>> getNextPage() {
+ AndroidFuture<AppSearchResult<List<SearchResult>>> nextPageFuture =
+ new AndroidFuture<>();
+
+ mSearchResults.getNextPage(mExecutor, nextPageFuture::complete);
+ return nextPageFuture.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(failedResultToException(result));
+ }
+ });
+ }
+ }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
new file mode 100644
index 0000000..e78f390
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
@@ -0,0 +1,203 @@
+/*
+ * 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.server.appfunctions;
+
+import static com.android.server.appfunctions.FutureAppSearchSession.failedResultToException;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchManager.SearchContext;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.BatchResultCallback;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.GetByDocumentIdRequest;
+import android.app.appsearch.GetSchemaResponse;
+import android.app.appsearch.PutDocumentsRequest;
+import android.app.appsearch.RemoveByDocumentIdRequest;
+import android.app.appsearch.SearchSpec;
+import android.app.appsearch.SetSchemaRequest;
+import android.app.appsearch.SetSchemaResponse;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** Implementation of {@link FutureAppSearchSession} */
+public class FutureAppSearchSessionImpl implements FutureAppSearchSession {
+
+ private static final String TAG = FutureAppSearchSession.class.getSimpleName();
+ private final Executor mExecutor;
+ private final AndroidFuture<AppSearchResult<AppSearchSession>> mSettableSessionFuture;
+
+ public FutureAppSearchSessionImpl(
+ @NonNull AppSearchManager appSearchManager,
+ @NonNull Executor executor,
+ @NonNull SearchContext appSearchContext) {
+ Objects.requireNonNull(appSearchManager);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(appSearchContext);
+
+ mExecutor = executor;
+ mSettableSessionFuture = new AndroidFuture<>();
+ appSearchManager.createSearchSession(
+ appSearchContext, mExecutor, mSettableSessionFuture::complete);
+ }
+
+ private AndroidFuture<AppSearchSession> getSessionAsync() {
+ return mSettableSessionFuture.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(failedResultToException(result));
+ }
+ });
+ }
+
+ @Override
+ public AndroidFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest setSchemaRequest) {
+ Objects.requireNonNull(setSchemaRequest);
+
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ AndroidFuture<AppSearchResult<SetSchemaResponse>>
+ settableSchemaResponse = new AndroidFuture<>();
+ session.setSchema(
+ setSchemaRequest,
+ mExecutor,
+ mExecutor,
+ settableSchemaResponse::complete);
+ return settableSchemaResponse.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(
+ failedResultToException(result));
+ }
+ });
+ });
+ }
+
+ @Override
+ public AndroidFuture<GetSchemaResponse> getSchema() {
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ AndroidFuture<AppSearchResult<GetSchemaResponse>>
+ settableSchemaResponse = new AndroidFuture<>();
+ session.getSchema(mExecutor, settableSchemaResponse::complete);
+ return settableSchemaResponse.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(
+ failedResultToException(result));
+ }
+ });
+ });
+ }
+
+ @Override
+ public AndroidFuture<AppSearchBatchResult<String, Void>> put(
+ @NonNull PutDocumentsRequest putDocumentsRequest) {
+ Objects.requireNonNull(putDocumentsRequest);
+
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ AndroidFuture<AppSearchBatchResult<String, Void>> batchResultFuture =
+ new AndroidFuture<>();
+
+ session.put(
+ putDocumentsRequest, mExecutor, batchResultFuture::complete);
+ return batchResultFuture;
+ });
+ }
+
+ @Override
+ public AndroidFuture<AppSearchBatchResult<String, Void>> remove(
+ @NonNull RemoveByDocumentIdRequest removeRequest) {
+ Objects.requireNonNull(removeRequest);
+
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ AndroidFuture<AppSearchBatchResult<String, Void>>
+ settableBatchResultFuture = new AndroidFuture<>();
+ session.remove(
+ removeRequest,
+ mExecutor,
+ new BatchResultCallbackAdapter<>(settableBatchResultFuture));
+ return settableBatchResultFuture;
+ });
+ }
+
+ @Override
+ public AndroidFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentId(
+ @NonNull GetByDocumentIdRequest getRequest) {
+ Objects.requireNonNull(getRequest);
+
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ AndroidFuture<AppSearchBatchResult<String, GenericDocument>>
+ batchResultFuture = new AndroidFuture<>();
+ session.getByDocumentId(
+ getRequest,
+ mExecutor,
+ new BatchResultCallbackAdapter<>(batchResultFuture));
+ return batchResultFuture;
+ });
+ }
+
+ @Override
+ public AndroidFuture<FutureSearchResults> search(
+ @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
+ return getSessionAsync()
+ .thenApply(session -> session.search(queryExpression, searchSpec))
+ .thenApply(result -> new FutureSearchResults(result, mExecutor));
+ }
+
+ @Override
+ public void close() throws IOException {}
+
+ private static final class BatchResultCallbackAdapter<K, V>
+ implements BatchResultCallback<K, V> {
+ private final AndroidFuture<AppSearchBatchResult<K, V>> mFuture;
+
+ BatchResultCallbackAdapter(AndroidFuture<AppSearchBatchResult<K, V>> future) {
+ mFuture = future;
+ }
+
+ @Override
+ public void onResult(@NonNull AppSearchBatchResult<K, V> result) {
+ mFuture.complete(result);
+ }
+
+ @Override
+ public void onSystemError(Throwable t) {
+ mFuture.completeExceptionally(t);
+ }
+ }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
new file mode 100644
index 0000000..0c22624
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
@@ -0,0 +1,94 @@
+/*
+ * 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.server.appfunctions;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.GlobalSearchSession;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.app.appsearch.observer.ObserverCallback;
+import android.app.appsearch.observer.ObserverSpec;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.Executor;
+
+/** A wrapper around {@link GlobalSearchSession} that provides a future-based API. */
+public class FutureGlobalSearchSession implements Closeable {
+ private static final String TAG = FutureGlobalSearchSession.class.getSimpleName();
+ private final Executor mExecutor;
+ private final AndroidFuture<AppSearchResult<GlobalSearchSession>> mSettableSessionFuture;
+
+ public FutureGlobalSearchSession(
+ @NonNull AppSearchManager appSearchManager, @NonNull Executor executor) {
+ this.mExecutor = executor;
+ mSettableSessionFuture = new AndroidFuture<>();
+ appSearchManager.createGlobalSearchSession(mExecutor, mSettableSessionFuture::complete);
+ }
+
+ private AndroidFuture<GlobalSearchSession> getSessionAsync() {
+ return mSettableSessionFuture.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(
+ FutureAppSearchSession.failedResultToException(result));
+ }
+ });
+ }
+
+ /**
+ * Registers an observer callback for the given target package name.
+ *
+ * @param targetPackageName The package name of the target app.
+ * @param spec The observer spec.
+ * @param executor The executor to run the observer callback on.
+ * @param observer The observer callback to register.
+ * @return A future that completes once the observer is registered.
+ */
+ public AndroidFuture<Void> registerObserverCallbackAsync(
+ String targetPackageName,
+ ObserverSpec spec,
+ Executor executor,
+ ObserverCallback observer) {
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ try {
+ session.registerObserverCallback(
+ targetPackageName, spec, executor, observer);
+ return AndroidFuture.completedFuture(null);
+ } catch (AppSearchException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ getSessionAsync().get().close();
+ } catch (Exception ex) {
+ Slog.e(TAG, "Failed to close global search session", ex);
+ }
+ }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
new file mode 100644
index 0000000..01e1008
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -0,0 +1,191 @@
+/*
+ * 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.server.appfunctions;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.app.appsearch.PropertyPath;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchSpec;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.appfunctions.FutureAppSearchSession.FutureSearchResults;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+/**
+ * This class implements helper methods for synchronously interacting with AppSearch while
+ * synchronizing AppFunction runtime and static metadata.
+ */
+public class MetadataSyncAdapter {
+ private final FutureAppSearchSession mFutureAppSearchSession;
+ private final Executor mSyncExecutor;
+
+ public MetadataSyncAdapter(
+ @NonNull Executor syncExecutor,
+ @NonNull FutureAppSearchSession futureAppSearchSession) {
+ mSyncExecutor = Objects.requireNonNull(syncExecutor);
+ mFutureAppSearchSession = Objects.requireNonNull(futureAppSearchSession);
+ }
+
+ /**
+ * This method returns a map of package names to a set of function ids that are in the static
+ * metadata but not in the runtime metadata.
+ *
+ * @param staticPackageToFunctionMap A map of package names to a set of function ids from the
+ * static metadata.
+ * @param runtimePackageToFunctionMap A map of package names to a set of function ids from the
+ * runtime metadata.
+ * @return A map of package names to a set of function ids that are in the static metadata but
+ * not in the runtime metadata.
+ */
+ @NonNull
+ @VisibleForTesting
+ static ArrayMap<String, ArraySet<String>> getAddedFunctionsDiffMap(
+ @NonNull ArrayMap<String, ArraySet<String>> staticPackageToFunctionMap,
+ @NonNull ArrayMap<String, ArraySet<String>> runtimePackageToFunctionMap) {
+ Objects.requireNonNull(staticPackageToFunctionMap);
+ Objects.requireNonNull(runtimePackageToFunctionMap);
+
+ return getFunctionsDiffMap(staticPackageToFunctionMap, runtimePackageToFunctionMap);
+ }
+
+ /**
+ * This method returns a map of package names to a set of function ids that are in the runtime
+ * metadata but not in the static metadata.
+ *
+ * @param staticPackageToFunctionMap A map of package names to a set of function ids from the
+ * static metadata.
+ * @param runtimePackageToFunctionMap A map of package names to a set of function ids from the
+ * runtime metadata.
+ * @return A map of package names to a set of function ids that are in the runtime metadata but
+ * not in the static metadata.
+ */
+ @NonNull
+ @VisibleForTesting
+ static ArrayMap<String, ArraySet<String>> getRemovedFunctionsDiffMap(
+ @NonNull ArrayMap<String, ArraySet<String>> staticPackageToFunctionMap,
+ @NonNull ArrayMap<String, ArraySet<String>> runtimePackageToFunctionMap) {
+ Objects.requireNonNull(staticPackageToFunctionMap);
+ Objects.requireNonNull(runtimePackageToFunctionMap);
+
+ return getFunctionsDiffMap(runtimePackageToFunctionMap, staticPackageToFunctionMap);
+ }
+
+ @NonNull
+ private static ArrayMap<String, ArraySet<String>> getFunctionsDiffMap(
+ @NonNull ArrayMap<String, ArraySet<String>> packageToFunctionMapA,
+ @NonNull ArrayMap<String, ArraySet<String>> packageToFunctionMapB) {
+ Objects.requireNonNull(packageToFunctionMapA);
+ Objects.requireNonNull(packageToFunctionMapB);
+
+ ArrayMap<String, ArraySet<String>> diffMap = new ArrayMap<>();
+ for (String packageName : packageToFunctionMapA.keySet()) {
+ if (!packageToFunctionMapB.containsKey(packageName)) {
+ diffMap.put(packageName, packageToFunctionMapA.get(packageName));
+ continue;
+ }
+ ArraySet<String> diffFunctions = new ArraySet<>();
+ for (String functionId :
+ Objects.requireNonNull(packageToFunctionMapA.get(packageName))) {
+ if (!Objects.requireNonNull(packageToFunctionMapB.get(packageName))
+ .contains(functionId)) {
+ diffFunctions.add(functionId);
+ }
+ }
+ if (!diffFunctions.isEmpty()) {
+ diffMap.put(packageName, diffFunctions);
+ }
+ }
+ return diffMap;
+ }
+
+ /**
+ * This method returns a map of package names to a set of function ids from the AppFunction
+ * metadata.
+ *
+ * @param schemaType The name space of the AppFunction metadata.
+ * @return A map of package names to a set of function ids from the AppFunction metadata.
+ */
+ @NonNull
+ @VisibleForTesting
+ @WorkerThread
+ ArrayMap<String, ArraySet<String>> getPackageToFunctionIdMap(
+ @NonNull String schemaType,
+ @NonNull String propertyFunctionId,
+ @NonNull String propertyPackageName)
+ throws ExecutionException, InterruptedException {
+ Objects.requireNonNull(schemaType);
+ Objects.requireNonNull(propertyFunctionId);
+ Objects.requireNonNull(propertyPackageName);
+ ArrayMap<String, ArraySet<String>> packageToFunctionIds = new ArrayMap<>();
+
+ FutureSearchResults futureSearchResults =
+ mFutureAppSearchSession
+ .search(
+ "",
+ buildMetadataSearchSpec(
+ schemaType, propertyFunctionId, propertyPackageName))
+ .get();
+ List<SearchResult> searchResultsList = futureSearchResults.getNextPage().get();
+ // TODO(b/357551503): This could be expensive if we have more functions
+ while (!searchResultsList.isEmpty()) {
+ for (SearchResult searchResult : searchResultsList) {
+ String packageName =
+ searchResult.getGenericDocument().getPropertyString(propertyPackageName);
+ String functionId =
+ searchResult.getGenericDocument().getPropertyString(propertyFunctionId);
+ packageToFunctionIds
+ .computeIfAbsent(packageName, k -> new ArraySet<>())
+ .add(functionId);
+ }
+ searchResultsList = futureSearchResults.getNextPage().get();
+ }
+ return packageToFunctionIds;
+ }
+
+ /**
+ * This method returns a {@link SearchSpec} for searching the AppFunction metadata.
+ *
+ * @param schemaType The schema type of the AppFunction metadata.
+ * @param propertyFunctionId The property name of the function id in the AppFunction metadata.
+ * @param propertyPackageName The property name of the package name in the AppFunction metadata.
+ * @return A {@link SearchSpec} for searching the AppFunction metadata.
+ */
+ @NonNull
+ private static SearchSpec buildMetadataSearchSpec(
+ @NonNull String schemaType,
+ @NonNull String propertyFunctionId,
+ @NonNull String propertyPackageName) {
+ Objects.requireNonNull(schemaType);
+ Objects.requireNonNull(propertyFunctionId);
+ Objects.requireNonNull(propertyPackageName);
+ return new SearchSpec.Builder()
+ .addFilterSchemas(schemaType)
+ .addProjectionPaths(
+ schemaType,
+ List.of(
+ new PropertyPath(propertyFunctionId),
+ new PropertyPath(propertyPackageName)))
+ .build();
+ }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
index 98903ae..58597c3 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
@@ -25,7 +25,6 @@
* services are properly unbound after the operation completes or a timeout occurs.
*
* @param <T> Class of wrapped service.
- * @hide
*/
public interface RemoteServiceCaller<T> {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
index c19a027..eea17ee 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
@@ -30,27 +30,24 @@
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
*/
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
deleted file mode 100644
index c01fe31..0000000
--- a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
+++ /dev/null
@@ -1,130 +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.server.appfunctions;
-
-import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
-
-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;
-import android.app.appsearch.AppSearchSession;
-import android.app.appsearch.GetSchemaResponse;
-import android.app.appsearch.SetSchemaRequest;
-import android.app.appsearch.SetSchemaResponse;
-import android.util.Slog;
-
-import com.android.internal.infra.AndroidFuture;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-
-/**
- * Helper class for interacting with a system server local appsearch session synchronously.
- */
-@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
-public class SyncAppSearchCallHelper implements Closeable {
- private static final String TAG = SyncAppSearchCallHelper.class.getSimpleName();
- private final Executor mExecutor;
- private final AppSearchManager mAppSearchManager;
- private final AndroidFuture<AppSearchResult<AppSearchSession>> mSettableSessionFuture;
-
- public SyncAppSearchCallHelper(@NonNull AppSearchManager appSearchManager,
- @NonNull Executor executor,
- @NonNull SearchContext appSearchContext) {
- Objects.requireNonNull(appSearchManager);
- Objects.requireNonNull(executor);
- Objects.requireNonNull(appSearchContext);
-
- mExecutor = executor;
- mAppSearchManager = appSearchManager;
- mSettableSessionFuture = new AndroidFuture<>();
- mAppSearchManager.createSearchSession(
- appSearchContext, mExecutor, mSettableSessionFuture::complete);
- }
-
- /**
- * Converts a failed app search result codes into an exception.
- */
- @NonNull
- private static 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());
- default -> new IllegalStateException(appSearchResult.getErrorMessage());
- };
- }
-
- private AppSearchSession getSession() throws Exception {
- AppSearchResult<AppSearchSession> sessionResult = mSettableSessionFuture.get();
- if (!sessionResult.isSuccess()) {
- throw failedResultToException(sessionResult);
- }
- return sessionResult.getResultValue();
- }
-
- /**
- * Gets the schema for a given app search session.
- */
- @WorkerThread
- public GetSchemaResponse getSchema() throws Exception {
- AndroidFuture<AppSearchResult<GetSchemaResponse>> settableSchemaResponse =
- new AndroidFuture<>();
- getSession().getSchema(mExecutor, settableSchemaResponse::complete);
- AppSearchResult<GetSchemaResponse> schemaResponse = settableSchemaResponse.get();
- if (schemaResponse.isSuccess()) {
- return schemaResponse.getResultValue();
- } else {
- throw failedResultToException(schemaResponse);
- }
- }
-
- /**
- * Sets the schema for a given app search session.
- */
- @WorkerThread
- public SetSchemaResponse setSchema(
- @NonNull SetSchemaRequest setSchemaRequest) throws Exception {
- AndroidFuture<AppSearchResult<SetSchemaResponse>> settableSchemaResponse =
- new AndroidFuture<>();
- getSession().setSchema(
- setSchemaRequest, mExecutor, mExecutor, settableSchemaResponse::complete);
- AppSearchResult<SetSchemaResponse> schemaResponse = settableSchemaResponse.get();
- if (schemaResponse.isSuccess()) {
- return schemaResponse.getResultValue();
- } else {
- throw failedResultToException(schemaResponse);
- }
- }
-
- @Override
- public void close() throws IOException {
- try {
- getSession().close();
- } catch (Exception ex) {
- Slog.e(TAG, "Failed to close app search session", ex);
- }
- }
-}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index de94715..b53bf98 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;
@@ -219,6 +220,15 @@
// See {@link Provider#pendingDeletedWidgetIds}.
private static final String PENDING_DELETED_IDS_ATTR = "pending_deleted_ids";
+ // Hard limit of number of hosts an app can create, note that the app that hosts the widgets
+ // can have multiple instances of {@link AppWidgetHost}, typically in respect to different
+ // surfaces in the host app.
+ // @see AppWidgetHost
+ // @see AppWidgetHost#mHostId
+ private static final int MAX_NUMBER_OF_HOSTS_PER_PACKAGE = 20;
+ // Hard limit of number of widgets can be pinned by a host.
+ private static final int MAX_NUMBER_OF_WIDGETS_PER_HOST = 200;
+
// Handles user and package related broadcasts.
// See {@link #registerBroadcastReceiver}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -1650,6 +1660,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);
@@ -2280,7 +2293,7 @@
if (host != null) {
return host;
}
-
+ ensureHostCountBeforeAddLocked(id);
host = new Host();
host.id = id;
mHosts.add(host);
@@ -2288,6 +2301,24 @@
return host;
}
+ /**
+ * Ensures that the number of hosts for a package is less than the maximum number of hosts per
+ * package. If the number of hosts is greater than the maximum number of hosts per package, then
+ * removes the oldest host.
+ */
+ private void ensureHostCountBeforeAddLocked(@NonNull final HostId hostId) {
+ final List<Host> hosts = new ArrayList<>();
+ for (Host host : mHosts) {
+ if (host.id.uid == hostId.uid
+ && host.id.packageName.equals(hostId.packageName)) {
+ hosts.add(host);
+ }
+ }
+ while (hosts.size() >= MAX_NUMBER_OF_HOSTS_PER_PACKAGE) {
+ deleteHostLocked(hosts.remove(0));
+ }
+ }
+
private void deleteHostLocked(Host host) {
if (DEBUG) {
Slog.i(TAG, "deleteHostLocked() " + host);
@@ -2373,6 +2404,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ mContext.unbindService(this);
+ }
+
+ @Override
public void onServiceDisconnected(ComponentName name) {
// Do nothing
}
@@ -2520,6 +2556,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ mContext.unbindService(this);
+ }
+
+ @Override
public void onServiceDisconnected(android.content.ComponentName name) {
// Do nothing
}
@@ -3578,12 +3619,33 @@
if (DEBUG) {
Slog.i(TAG, "addWidgetLocked() " + widget);
}
+ ensureWidgetCountBeforeAddLocked(widget);
mWidgets.add(widget);
onWidgetProviderAddedOrChangedLocked(widget);
}
/**
+ * Ensures that the widget count for the widget's host is not greater than the maximum
+ * number of widgets per host. If the count is greater than the maximum, removes oldest widgets
+ * from the host until the count is less than or equal to the maximum.
+ */
+ private void ensureWidgetCountBeforeAddLocked(@NonNull final Widget widget) {
+ if (widget.host == null || widget.host.id == null) {
+ return;
+ }
+ final List<Widget> widgetsInSameHost = new ArrayList<>();
+ for (Widget w : mWidgets) {
+ if (w.host != null && widget.host.id.equals(w.host.id)) {
+ widgetsInSameHost.add(w);
+ }
+ }
+ while (widgetsInSameHost.size() >= MAX_NUMBER_OF_WIDGETS_PER_HOST) {
+ removeWidgetLocked(widgetsInSameHost.remove(0));
+ }
+ }
+
+ /**
* Checks if the provider is assigned and updates the mWidgetPackages to track packages
* that have bound widgets.
*/
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 5044e93..2c261fe 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -842,9 +842,13 @@
+ "event");
return;
}
+
PresentationStatsEventInternal event = mEventInternal.get();
+ boolean ignoreLogging = !event.mIsDatasetAvailable;
+
if (sVerbose) {
Slog.v(TAG, "(" + caller + ") "
+ + (ignoreLogging ? "IGNORING - following event won't be logged: " : "")
+ "Log AutofillPresentationEventReported:"
+ " requestId=" + event.mRequestId
+ " sessionId=" + mSessionId
@@ -907,7 +911,8 @@
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
- if (!event.mIsDatasetAvailable) {
+ if (ignoreLogging) {
+ Slog.w(TAG, "Empty dataset. Autofill ignoring log");
mEventInternal = Optional.empty();
return;
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 14a3211..dc0b4b8 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -48,6 +48,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
import android.widget.BaseAdapter;
@@ -82,7 +83,6 @@
com.android.internal.R.style.Theme_DeviceDefault_Light_Autofill;
private static final int THEME_ID_DARK =
com.android.internal.R.style.Theme_DeviceDefault_Autofill;
- private static final int AUTOFILL_CREDMAN_MAX_VISIBLE_DATASETS = 5;
private static final TypedValue sTempTypedValue = new TypedValue();
@@ -113,9 +113,11 @@
private final @NonNull Callback mCallback;
+ private final @NonNull WindowManager mWindowManager;
+
private final @Nullable View mHeader;
private final @NonNull ListView mListView;
- private final @Nullable View mFooter;
+ private @Nullable View mFooter;
private final @Nullable ItemsAdapter mAdapter;
@@ -134,6 +136,8 @@
private int mMaxInputLengthForAutofill;
+ private final boolean mIsCredmanAutofillSession;
+
public static boolean isFullScreen(Context context) {
if (sFullScreenMode != null) {
if (sVerbose) Slog.v(TAG, "forcing full-screen mode to " + sFullScreenMode);
@@ -158,6 +162,9 @@
mContext = new ContextThemeWrapper(context, mThemeId);
mUserContext = Helper.getUserContext(mContext);
mMaxInputLengthForAutofill = maxInputLengthForAutofill;
+ mIsCredmanAutofillSession = (Flags.autofillCredmanIntegration()
+ && ((response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0));
+ mWindowManager = mContext.getSystemService(WindowManager.class);
final LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -167,7 +174,8 @@
final ViewGroup decor;
if (mFullScreen) {
decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
- } else if (headerPresentation != null || footerPresentation != null) {
+ } else if (headerPresentation != null
+ || footerPresentation != null || mIsCredmanAutofillSession) {
decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_header_footer,
null);
} else {
@@ -219,11 +227,7 @@
if (sVerbose) {
Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
}
- } else if (Flags.autofillCredmanIntegration() && (
- (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0)) {
- mVisibleDatasetsMaxCount = AUTOFILL_CREDMAN_MAX_VISIBLE_DATASETS;
- }
- else {
+ } else {
mVisibleDatasetsMaxCount = mContext.getResources()
.getInteger(com.android.internal.R.integer.autofill_max_visible_datasets);
}
@@ -301,7 +305,7 @@
mHeader = null;
}
- if (footerPresentation != null) {
+ if (footerPresentation != null && !mIsCredmanAutofillSession) {
final LinearLayout footerContainer =
decor.findViewById(R.id.autofill_dataset_footer);
if (footerContainer != null) {
@@ -366,7 +370,22 @@
}
applyCancelAction(view, response.getCancelIds());
- items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
+ if (AutofillManager.PINNED_DATASET_ID.equals(dataset.getId())
+ && mIsCredmanAutofillSession && !items.isEmpty()) {
+ final LinearLayout footerContainer =
+ decor.findViewById(R.id.autofill_dataset_footer);
+ if (sVerbose) {
+ Slog.v(TAG, "adding footer");
+ }
+ mFooter = view;
+ footerContainer.addView(mFooter);
+ footerContainer.setVisibility(View.VISIBLE);
+ footerContainer.setClickable(true);
+ footerContainer.setOnClickListener(v -> mCallback.onDatasetPicked(dataset));
+ } else {
+ items.add(
+ new ViewItem(dataset, filterPattern, filterable, valueText, view));
+ }
}
}
@@ -459,12 +478,9 @@
if (updateContentSize()) {
requestShowFillUi();
}
- if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
- mListView.setVerticalScrollBarEnabled(true);
- mListView.onVisibilityAggregated(true);
- } else {
- mListView.setVerticalScrollBarEnabled(false);
- }
+ mListView.setVerticalScrollBarEnabled(true);
+ mListView.onVisibilityAggregated(true);
+
if (mAdapter.getCount() != oldCount) {
mListView.requestLayout();
}
@@ -578,11 +594,18 @@
return changed;
}
+ private boolean heightLesserThanDisplayScreen(int height) {
+ // Don't update list height for credential options beyond 80% of display window even if we
+ // are still under the max visible number of datasets. This could happen when font or
+ // display size is set to large.
+ return height < (0.8 * mWindowManager.getCurrentWindowMetrics().getBounds().height());
+ }
+
private boolean updateHeight(View view, Point maxSize) {
boolean changed = false;
final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
final int newContentHeight = mContentHeight + clampedMeasuredHeight;
- if (newContentHeight != mContentHeight) {
+ if (newContentHeight != mContentHeight && heightLesserThanDisplayScreen(newContentHeight)) {
mContentHeight = newContentHeight;
changed = true;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 2119622..4b9065b 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -27,7 +27,6 @@
import android.annotation.UserIdInt;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
-import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.AttributionSource;
@@ -61,6 +60,9 @@
private static final String TAG = "GenericWindowPolicyController";
+ private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
+ new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+
/** Interface to listen running applications change on virtual display. */
public interface RunningAppsChangedListener {
/**
@@ -69,29 +71,25 @@
void onRunningAppsChanged(ArraySet<Integer> runningUids);
}
- /**
- * For communicating when activities are blocked from running on the display by this policy
- * controller.
- */
- public interface ActivityBlockedCallback {
+ /** Interface to react to activity changes on the virtual display. */
+ public interface ActivityListener {
+
+ /** Called when the top activity changes. */
+ void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
+ @UserIdInt int userId);
+
+ /** Called when the display becomes empty. */
+ void onDisplayEmpty(int displayId);
+
/** Called when an activity is blocked.*/
- void onActivityBlocked(int displayId, ActivityInfo activityInfo, IntentSender intentSender);
- }
- private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
- new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+ void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo,
+ @Nullable IntentSender intentSender);
- /**
- * For communicating when a secure window shows on the virtual display.
- */
- public interface SecureWindowCallback {
/** Called when a secure window shows on the virtual display. */
- void onSecureWindowShown(int displayId, int uid);
- }
+ void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo);
- /** Interface to listen for interception of intents. */
- public interface IntentListenerCallback {
/** Returns true when an intent should be intercepted */
- boolean shouldInterceptIntent(Intent intent);
+ boolean shouldInterceptIntent(@NonNull Intent intent);
}
/**
@@ -118,7 +116,6 @@
private final ArraySet<ComponentName> mCrossTaskNavigationExemptions;
@NonNull
private final Object mGenericWindowPolicyControllerLock = new Object();
- @Nullable private final ActivityBlockedCallback mActivityBlockedCallback;
// Do not access mDisplayId and mIsMirrorDisplay directly, instead use waitAndGetDisplayId()
// and waitAndGetIsMirrorDisplay()
@@ -129,14 +126,12 @@
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
private final ArraySet<Integer> mRunningUids = new ArraySet<>();
- @Nullable private final ActivityListener mActivityListener;
- @Nullable private final IntentListenerCallback mIntentListenerCallback;
+ @NonNull private final ActivityListener mActivityListener;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners =
new ArraySet<>();
- @Nullable private final SecureWindowCallback mSecureWindowCallback;
@NonNull private final Set<String> mDisplayCategories;
@GuardedBy("mGenericWindowPolicyControllerLock")
@@ -162,12 +157,6 @@
* @param crossTaskNavigationExemptions The set of components explicitly exempt from the default
* navigation policy.
* @param activityListener Activity listener to listen for activity changes.
- * @param activityBlockedCallback Callback that is called when an activity is blocked from
- * launching.
- * @param secureWindowCallback Callback that is called when a secure window shows on the
- * virtual display.
- * @param intentListenerCallback Callback that is called to intercept intents when matching
- * passed in filters.
* @param showTasksInHostDeviceRecents whether to show activities in recents on the host device.
* @param customHomeComponent The component acting as a home activity on the virtual display. If
* {@code null}, then the system-default secondary home activity will be used. This is only
@@ -184,10 +173,7 @@
@NonNull Set<String> activityPolicyPackageExemptions,
boolean crossTaskNavigationAllowedByDefault,
@NonNull Set<ComponentName> crossTaskNavigationExemptions,
- @Nullable ActivityListener activityListener,
- @Nullable ActivityBlockedCallback activityBlockedCallback,
- @Nullable SecureWindowCallback secureWindowCallback,
- @Nullable IntentListenerCallback intentListenerCallback,
+ @NonNull ActivityListener activityListener,
@NonNull Set<String> displayCategories,
boolean showTasksInHostDeviceRecents,
@Nullable ComponentName customHomeComponent) {
@@ -199,11 +185,8 @@
mActivityPolicyPackageExemptions = new ArraySet<>(activityPolicyPackageExemptions);
mCrossTaskNavigationAllowedByDefault = crossTaskNavigationAllowedByDefault;
mCrossTaskNavigationExemptions = new ArraySet<>(crossTaskNavigationExemptions);
- mActivityBlockedCallback = activityBlockedCallback;
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
- mSecureWindowCallback = secureWindowCallback;
- mIntentListenerCallback = intentListenerCallback;
mDisplayCategories = displayCategories;
mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents;
mCustomHomeComponent = customHomeComponent;
@@ -306,8 +289,7 @@
@Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
int launchingFromDisplayId, boolean isNewTask, boolean isResultExpected,
@Nullable Supplier<IntentSender> intentSender) {
- if (mIntentListenerCallback != null && intent != null
- && mIntentListenerCallback.shouldInterceptIntent(intent)) {
+ if (intent != null && mActivityListener.shouldInterceptIntent(intent)) {
logActivityLaunchBlocked("Virtual device intercepting intent");
return false;
}
@@ -391,11 +373,9 @@
int displayId = waitAndGetDisplayId();
// The callback is fired only when windowFlags are changed. To let VirtualDevice owner
// aware that the virtual display has a secure window on top.
- if ((windowFlags & FLAG_SECURE) != 0 && mSecureWindowCallback != null
- && displayId != INVALID_DISPLAY) {
+ if ((windowFlags & FLAG_SECURE) != 0 && displayId != INVALID_DISPLAY) {
// Post callback on the main thread, so it doesn't block activity launching.
- mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(displayId,
- activityInfo.applicationInfo.uid));
+ mHandler.post(() -> mActivityListener.onSecureWindowShown(displayId, activityInfo));
}
if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
@@ -418,7 +398,7 @@
// Don't send onTopActivityChanged() callback when topActivity is null because it's defined
// as @NonNull in ActivityListener interface. Sends onDisplayEmpty() callback instead when
// there is no activity running on virtual display.
- if (mActivityListener != null && topActivity != null && displayId != INVALID_DISPLAY) {
+ if (topActivity != null && displayId != INVALID_DISPLAY) {
// Post callback on the main thread so it doesn't block activity launching
mHandler.post(() ->
mActivityListener.onTopActivityChanged(displayId, topActivity, userId));
@@ -431,8 +411,7 @@
mRunningUids.clear();
mRunningUids.addAll(runningUids);
int displayId = waitAndGetDisplayId();
- if (mActivityListener != null && mRunningUids.isEmpty()
- && displayId != INVALID_DISPLAY) {
+ if (mRunningUids.isEmpty() && displayId != INVALID_DISPLAY) {
// Post callback on the main thread so it doesn't block activity launching
mHandler.post(() -> mActivityListener.onDisplayEmpty(displayId));
}
@@ -482,9 +461,8 @@
int displayId = waitAndGetDisplayId();
// Don't trigger activity blocked callback for mirror displays, because we can't show
// any activity or presentation on it anyway.
- if (!waitAndGetIsMirrorDisplay() && mActivityBlockedCallback != null
- && displayId != INVALID_DISPLAY) {
- mActivityBlockedCallback.onActivityBlocked(displayId, activityInfo,
+ if (!waitAndGetIsMirrorDisplay() && displayId != INVALID_DISPLAY) {
+ mActivityListener.onActivityLaunchBlocked(displayId, activityInfo,
intentSender == null ? null : intentSender.get());
}
Counter.logIncrementWithUid(
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 4eb50a9..cd2dd3a 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -40,6 +40,7 @@
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
+import android.app.compat.CompatChanges;
import android.companion.AssociationInfo;
import android.companion.virtual.ActivityPolicyExemption;
import android.companion.virtual.IVirtualDevice;
@@ -48,7 +49,6 @@
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
-import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -56,6 +56,8 @@
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
@@ -88,6 +90,7 @@
import android.media.AudioManager;
import android.media.audiopolicy.AudioMix;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
@@ -132,6 +135,16 @@
private static final String TAG = "VirtualDeviceImpl";
+ /**
+ * Do not show a toast on the virtual display when a secure surface is shown after
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}. VDM clients should use
+ * {@link VirtualDeviceManager.ActivityListener#onSecureWindowShown} instead to provide
+ * a custom notification if desired.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long DO_NOT_SHOW_TOAST_WHEN_SECURE_SURFACE_SHOWN = 311101667L;
+
private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
DisplayManager.VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED
| DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
@@ -182,7 +195,7 @@
@GuardedBy("mVirtualDeviceLock")
private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>();
private IVirtualDeviceActivityListener mActivityListener;
- private ActivityListener mActivityListenerAdapter = null;
+ private GenericWindowPolicyController.ActivityListener mActivityListenerAdapter = null;
private IVirtualDeviceSoundEffectListener mSoundEffectListener;
private final DisplayManagerGlobal mDisplayManager;
private final DisplayManagerInternal mDisplayManagerInternal;
@@ -207,50 +220,122 @@
@NonNull
private final Set<String> mActivityPolicyPackageExemptions = new ArraySet<>();
- private ActivityListener createListenerAdapter() {
- return new ActivityListener() {
+ private class GwpcActivityListener implements GenericWindowPolicyController.ActivityListener {
- @Override
- public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity) {
- try {
- mActivityListener.onTopActivityChanged(displayId, topActivity,
- UserHandle.USER_NULL);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
- }
+ @Override
+ public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
+ @UserIdInt int userId) {
+ try {
+ mActivityListener.onTopActivityChanged(displayId, topActivity, userId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+ }
+ }
+
+ @Override
+ public void onDisplayEmpty(int displayId) {
+ try {
+ mActivityListener.onDisplayEmpty(displayId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+ }
+ }
+
+ @Override
+ public void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo,
+ @Nullable IntentSender intentSender) {
+ Intent intent =
+ BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName());
+ if (shouldShowBlockedActivityDialog(
+ activityInfo.getComponentName(), intent.getComponent())) {
+ mContext.startActivityAsUser(
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
+ ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
+ UserHandle.SYSTEM);
}
- @Override
- public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
- @UserIdInt int userId) {
- try {
- mActivityListener.onTopActivityChanged(displayId, topActivity, userId);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
- }
- }
-
- @Override
- public void onDisplayEmpty(int displayId) {
- try {
- mActivityListener.onDisplayEmpty(displayId);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
- }
- }
-
- @Override
- public void onActivityLaunchBlocked(int displayId,
- @NonNull ComponentName componentName, @NonNull UserHandle user,
- @Nullable IntentSender intentSender) {
+ if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
try {
mActivityListener.onActivityLaunchBlocked(
- displayId, componentName, user, intentSender);
+ displayId,
+ activityInfo.getComponentName(),
+ UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid),
+ intentSender);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
}
}
- };
+ }
+
+ @Override
+ public void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo) {
+ if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+ try {
+ mActivityListener.onSecureWindowShown(
+ displayId,
+ activityInfo.getComponentName(),
+ UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+ }
+
+ if (CompatChanges.isChangeEnabled(DO_NOT_SHOW_TOAST_WHEN_SECURE_SURFACE_SHOWN,
+ mOwnerPackageName, UserHandle.getUserHandleForUid(mOwnerUid))) {
+ return;
+ }
+ }
+
+ // If a virtual display isn't secure, the screen can't be captured. Show a warning toast
+ // if the secure window is shown on a non-secure virtual display.
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ Display display = displayManager.getDisplay(displayId);
+ if ((display.getFlags() & Display.FLAG_SECURE) == 0) {
+ showToastWhereUidIsRunning(activityInfo.applicationInfo.uid,
+ com.android.internal.R.string.vdm_secure_window,
+ Toast.LENGTH_LONG, mContext.getMainLooper());
+
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_secure_window_blocked_count",
+ mAttributionSource.getUid());
+ }
+ }
+
+ /**
+ * Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true
+ * if the intent matches any filter notifying the DisplayPolicyController to abort the
+ * activity launch to be replaced by the interception.
+ */
+ @Override
+ public boolean shouldInterceptIntent(@NonNull Intent intent) {
+ synchronized (mVirtualDeviceLock) {
+ boolean hasInterceptedIntent = false;
+ for (Map.Entry<IBinder, IntentFilter> interceptor
+ : mIntentInterceptors.entrySet()) {
+ IntentFilter intentFilter = interceptor.getValue();
+ // Explicitly match the actions because the intent filter will match any intent
+ // without an explicit action. If the intent has no action, then require that
+ // there are no actions specified in the filter either.
+ boolean explicitActionMatch =
+ intent.getAction() != null || intentFilter.countActions() == 0;
+ if (explicitActionMatch && intentFilter.match(
+ intent.getAction(), intent.getType(), intent.getScheme(),
+ intent.getData(), intent.getCategories(), TAG) >= 0) {
+ try {
+ // For privacy reasons, only returning the intents action and data.
+ // Any other required field will require a review.
+ IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey())
+ .onIntentIntercepted(
+ new Intent(intent.getAction(), intent.getData()));
+ hasInterceptedIntent = true;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener", e);
+ }
+ }
+ }
+ return hasInterceptedIntent;
+ }
+ }
}
VirtualDeviceImpl(
@@ -1290,7 +1375,7 @@
Flags.vdmCustomHome() ? mParams.getHomeComponent() : null;
if (mActivityListenerAdapter == null) {
- mActivityListenerAdapter = createListenerAdapter();
+ mActivityListenerAdapter = new GwpcActivityListener();
}
final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(
@@ -1306,9 +1391,6 @@
? mParams.getBlockedCrossTaskNavigations()
: mParams.getAllowedCrossTaskNavigations(),
mActivityListenerAdapter,
- this::onActivityBlocked,
- this::onSecureWindowShown,
- this::shouldInterceptIntent,
displayCategories,
showTasksInHostDeviceRecents,
homeComponent);
@@ -1378,28 +1460,6 @@
}
}
- @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
- private void onActivityBlocked(int displayId, ActivityInfo activityInfo,
- IntentSender intentSender) {
- Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName());
- if (shouldShowBlockedActivityDialog(
- activityInfo.getComponentName(), intent.getComponent())) {
- mContext.startActivityAsUser(
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
- ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
- UserHandle.SYSTEM);
- }
-
- if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
- mActivityListenerAdapter.onActivityLaunchBlocked(
- displayId,
- activityInfo.getComponentName(),
- UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid),
- intentSender);
- }
- }
-
private boolean shouldShowBlockedActivityDialog(ComponentName blockedComponent,
ComponentName blockedAppStreamingActivityComponent) {
if (Objects.equals(blockedComponent, blockedAppStreamingActivityComponent)) {
@@ -1414,27 +1474,6 @@
return getDevicePolicy(POLICY_TYPE_BLOCKED_ACTIVITY) == DEVICE_POLICY_DEFAULT;
}
- private void onSecureWindowShown(int displayId, int uid) {
- synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplays.contains(displayId)) {
- return;
- }
- }
-
- // If a virtual display isn't secure, the screen can't be captured. Show a warning toast
- // if the secure window is shown on a non-secure virtual display.
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(displayId);
- if ((display.getFlags() & Display.FLAG_SECURE) == 0) {
- showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window,
- Toast.LENGTH_LONG, mContext.getMainLooper());
-
- Counter.logIncrementWithUid(
- "virtual_devices.value_secure_window_blocked_count",
- mAttributionSource.getUid());
- }
- }
-
private ArraySet<UserHandle> getAllowedUserHandles() {
ArraySet<UserHandle> result = new ArraySet<>();
final long token = Binder.clearCallingIdentity();
@@ -1621,40 +1660,6 @@
}
}
- /**
- * Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true if
- * the intent matches any filter notifying the DisplayPolicyController to abort the
- * activity launch to be replaced by the interception.
- */
- private boolean shouldInterceptIntent(Intent intent) {
- synchronized (mVirtualDeviceLock) {
- boolean hasInterceptedIntent = false;
- for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) {
- IntentFilter intentFilter = interceptor.getValue();
- // Explicitly match the actions because the intent filter will match any intent
- // without an explicit action. If the intent has no action, then require that there
- // are no actions specified in the filter either.
- boolean explicitActionMatch =
- intent.getAction() != null || intentFilter.countActions() == 0;
- if (explicitActionMatch && intentFilter.match(
- intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(),
- intent.getCategories(), TAG) >= 0) {
- try {
- // For privacy reasons, only returning the intents action and data. Any
- // other required field will require a review.
- IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey())
- .onIntentIntercepted(new Intent(intent.getAction(), intent.getData()));
- hasInterceptedIntent = true;
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mVirtualDeviceIntentInterceptor", e);
- }
- }
- }
-
- return hasInterceptedIntent;
- }
- }
-
interface PendingTrampolineCallback {
/**
* Called when the callback should start waiting for the given pending trampoline.
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 1470e9a..6657c1c 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -238,13 +238,16 @@
final SomeArgs args = (SomeArgs) msg.obj;
final Context context;
final Intent intent;
+ final boolean forceUpdate;
try {
context = (Context) args.arg1;
intent = (Intent) args.arg2;
+ forceUpdate = (Boolean) args.arg3;
} finally {
args.recycle();
}
- broadcastBatteryChangedIntent(context, intent, BATTERY_CHANGED_OPTIONS);
+ broadcastBatteryChangedIntent(context, intent, BATTERY_CHANGED_OPTIONS,
+ forceUpdate);
return true;
}
case MSG_BROADCAST_POWER_CONNECTION_CHANGED: {
@@ -798,7 +801,7 @@
// We are doing this after sending the above broadcasts, so anything processing
// them will get the new sequence number at that point. (See for example how testing
// of JobScheduler's BatteryController works.)
- sendBatteryChangedIntentLocked();
+ sendBatteryChangedIntentLocked(force);
if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) {
sendBatteryLevelChangedIntentLocked();
}
@@ -829,7 +832,7 @@
}
}
- private void sendBatteryChangedIntentLocked() {
+ private void sendBatteryChangedIntentLocked(boolean forceUpdate) {
// Pack up the values and broadcast them to everyone
final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -869,15 +872,17 @@
final SomeArgs args = SomeArgs.obtain();
args.arg1 = mContext;
args.arg2 = intent;
+ args.arg3 = forceUpdate;
mHandler.obtainMessage(MSG_BROADCAST_BATTERY_CHANGED, args).sendToTarget();
} else {
mHandler.post(() -> broadcastBatteryChangedIntent(mContext,
- intent, BATTERY_CHANGED_OPTIONS));
+ intent, BATTERY_CHANGED_OPTIONS, forceUpdate));
}
}
private static void broadcastBatteryChangedIntent(Context context, Intent intent,
- Bundle options) {
+ Bundle options, boolean forceUpdate) {
+ traceBatteryChangedBroadcastEvent(intent, forceUpdate);
// TODO (293959093): It is important that SystemUI receives this broadcast as soon as
// possible. Ideally, it should be using binder callbacks but until then, dispatch this
// as a foreground broadcast to SystemUI.
@@ -895,6 +900,53 @@
AppOpsManager.OP_NONE, options, UserHandle.USER_ALL);
}
+ private static void traceBatteryChangedBroadcastEvent(Intent intent, boolean forceUpdate) {
+ if (!com.android.server.flags.Flags.traceBatteryChangedBroadcastEvent()) {
+ return;
+ }
+ if (!Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) return;
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append("broadcastBatteryChanged; ");
+ builder.append("force="); builder.append(forceUpdate);
+ builder.append(",seq="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_SEQUENCE, -1));
+ builder.append(",s="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_STATUS, -1));
+ builder.append(",h="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_HEALTH, -1));
+ builder.append(",p="); builder.append(intent.getBooleanExtra(
+ BatteryManager.EXTRA_PRESENT, false));
+ builder.append(",l="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_LEVEL, -1));
+ builder.append(",bl="); builder.append(intent.getBooleanExtra(
+ BatteryManager.EXTRA_BATTERY_LOW, false));
+ builder.append(",sc="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_SCALE, -1));
+ builder.append(",pt="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_PLUGGED, -1));
+ builder.append(",v="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_VOLTAGE, -1));
+ builder.append(",t="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_TEMPERATURE, -1));
+ builder.append(",tech="); builder.append(intent.getStringExtra(
+ BatteryManager.EXTRA_TECHNOLOGY));
+ builder.append(",invc="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_INVALID_CHARGER, -1));
+ builder.append(",mcc="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_MAX_CHARGING_CURRENT, -1));
+ builder.append(",mcv="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, -1));
+ builder.append(",chc="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_CHARGE_COUNTER, -1));
+ builder.append(",cc="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_CYCLE_COUNT, -1));
+ builder.append(",chs="); builder.append(intent.getIntExtra(
+ BatteryManager.EXTRA_CHARGING_STATUS, -1));
+
+ Trace.instant(Trace.TRACE_TAG_SYSTEM_SERVER, builder.toString());
+ }
+
private void sendBatteryLevelChangedIntentLocked() {
Bundle event = new Bundle();
long now = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 47203fb..fbe593f 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -20,6 +20,8 @@
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
+import static com.android.server.crashrecovery.CrashRecoveryUtils.dumpCrashRecoveryEvents;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
@@ -44,6 +46,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.Xml;
@@ -51,7 +54,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -72,6 +74,7 @@
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -1265,18 +1268,21 @@
/** Dump status of every observer in mAllObservers. */
- public void dump(IndentingPrintWriter pw) {
- pw.println("Package Watchdog status");
- pw.increaseIndent();
+ public void dump(PrintWriter pw) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("Package Watchdog status");
+ ipw.increaseIndent();
synchronized (mLock) {
for (String observerName : mAllObservers.keySet()) {
- pw.println("Observer name: " + observerName);
- pw.increaseIndent();
+ ipw.println("Observer name: " + observerName);
+ ipw.increaseIndent();
ObserverInternal observerInternal = mAllObservers.get(observerName);
- observerInternal.dump(pw);
- pw.decreaseIndent();
+ observerInternal.dump(ipw);
+ ipw.decreaseIndent();
}
}
+ ipw.decreaseIndent();
+ dumpCrashRecoveryEvents(ipw);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index bba97fa..cadceb5 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -18,7 +18,7 @@
import static android.provider.DeviceConfig.Properties;
-import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -291,13 +291,13 @@
Properties properties = new Properties.Builder(namespaceToReset).build();
try {
if (!DeviceConfig.setProperties(properties)) {
- logCriticalInfo(Log.ERROR, "Failed to clear properties under "
+ logCrashRecoveryEvent(Log.ERROR, "Failed to clear properties under "
+ namespaceToReset
+ ". Running `device_config get_sync_disabled_for_tests` will confirm"
+ " if config-bulk-update is enabled.");
}
} catch (DeviceConfig.BadConfigException exception) {
- logCriticalInfo(Log.WARN, "namespace " + namespaceToReset
+ logCrashRecoveryEvent(Log.WARN, "namespace " + namespaceToReset
+ " is already banned, skip reset.");
}
}
@@ -528,7 +528,7 @@
if (!TextUtils.isEmpty(failedPackage)) {
successMsg += " for package " + failedPackage;
}
- logCriticalInfo(Log.DEBUG, successMsg);
+ logCrashRecoveryEvent(Log.DEBUG, successMsg);
} catch (Throwable t) {
logRescueException(level, failedPackage, t);
}
@@ -687,7 +687,7 @@
if (!TextUtils.isEmpty(failedPackageName)) {
failureMsg += " for package " + failedPackageName;
}
- logCriticalInfo(Log.ERROR, failureMsg + ": " + msg);
+ logCrashRecoveryEvent(Log.ERROR, failureMsg + ": " + msg);
}
private static int mapRescueLevelToUserImpact(int rescueLevel) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 07e5f2e..d86bae1 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -224,6 +224,9 @@
/** Extended timeout for the system server watchdog for vold#partition operation. */
private static final int PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS = 3 * 60 * 1000;
+ private static final Pattern OBB_FILE_PATH = Pattern.compile(
+ "(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/obb/)([^/]+)/([^/]+\\.obb)");
+
@GuardedBy("mLock")
private final Set<Integer> mFuseMountedUser = new ArraySet<>();
@@ -3144,7 +3147,9 @@
Objects.requireNonNull(rawPath, "rawPath cannot be null");
Objects.requireNonNull(canonicalPath, "canonicalPath cannot be null");
Objects.requireNonNull(token, "token cannot be null");
- Objects.requireNonNull(obbInfo, "obbIfno cannot be null");
+ Objects.requireNonNull(obbInfo, "obbInfo cannot be null");
+
+ validateObbInfo(obbInfo, rawPath);
final int callingUid = Binder.getCallingUid();
final ObbState obbState = new ObbState(rawPath, canonicalPath,
@@ -3156,6 +3161,34 @@
Slog.i(TAG, "Send to OBB handler: " + action.toString());
}
+ private void validateObbInfo(ObbInfo obbInfo, String rawPath) {
+ String obbFilePath;
+ try {
+ obbFilePath = new File(rawPath).getCanonicalPath();
+ } catch (IOException ex) {
+ throw new RuntimeException("Failed to resolve path" + rawPath + " : " + ex);
+ }
+
+ Matcher matcher = OBB_FILE_PATH.matcher(obbFilePath);
+
+ if (matcher.matches()) {
+ int userId = UserHandle.getUserId(Binder.getCallingUid());
+ String pathUserId = matcher.group(2);
+ String pathPackageName = matcher.group(3);
+ if ((pathUserId != null && Integer.parseInt(pathUserId) != userId)
+ || (pathUserId == null && userId != mCurrentUserId)) {
+ throw new SecurityException(
+ "Path " + obbFilePath + "does not correspond to calling userId " + userId);
+ }
+ if (obbInfo != null && !obbInfo.packageName.equals(pathPackageName)) {
+ throw new SecurityException("Path " + obbFilePath
+ + " does not contain package name " + pathPackageName);
+ }
+ } else {
+ throw new SecurityException("Invalid path to Obb file : " + obbFilePath);
+ }
+ }
+
@Override
public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
Objects.requireNonNull(rawPath, "rawPath cannot be null");
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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d4f729c..3666524 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1516,9 +1516,8 @@
serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START);
mAm.mBatteryStatsService.noteServiceStartRunning(uid, packageName, serviceName);
final ProcessRecord hostApp = r.app;
- final boolean wasStopped = hostApp == null ? wasStopped(r) : false;
- final boolean firstLaunch =
- hostApp == null ? !mAm.wasPackageEverLaunched(r.packageName, r.userId) : false;
+ final boolean wasStopped = hostApp == null ? r.appInfo.isStopped() : false;
+ final boolean firstLaunch = hostApp == null ? r.appInfo.isNotLaunched() : false;
String error = bringUpServiceLocked(r, service.getFlags(), callerFg,
false /* whileRestarting */,
@@ -4308,9 +4307,8 @@
true, UNKNOWN_ADJ);
}
- final boolean wasStopped = hostApp == null ? wasStopped(s) : false;
- final boolean firstLaunch =
- hostApp == null ? !mAm.wasPackageEverLaunched(s.packageName, s.userId) : false;
+ final boolean wasStopped = hostApp == null ? s.appInfo.isStopped() : false;
+ final boolean firstLaunch = hostApp == null ? s.appInfo.isNotLaunched() : false;
boolean needOomAdj = false;
if (c.hasFlag(Context.BIND_AUTO_CREATE)) {
@@ -9350,8 +9348,4 @@
return mCachedDeviceProvisioningPackage != null
&& mCachedDeviceProvisioningPackage.equals(packageName);
}
-
- private boolean wasStopped(ServiceRecord serviceRecord) {
- return (serviceRecord.appInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0;
- }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d121535..3c57476 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -299,7 +299,6 @@
import android.content.pm.UserInfo;
import android.content.pm.UserProperties;
import android.content.pm.VersionedPackage;
-import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -2971,10 +2970,6 @@
}
}
- CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
- return mAtmInternal.compatibilityInfoForPackage(ai);
- }
-
/**
* Enforces that the uid that calls a method is not an
* {@link UserHandle#isIsolated(int) isolated} uid.
@@ -4635,7 +4630,6 @@
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s",
processName, app.getWindowProcessController().getConfiguration());
ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
- app.setCompat(compatibilityInfoForPackage(appInfo));
ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr);
@@ -4674,7 +4668,9 @@
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
bindApplicationTimeMillis = SystemClock.uptimeMillis();
bindApplicationTimeNanos = SystemClock.uptimeNanos();
- mAtmInternal.preBindApplication(app.getWindowProcessController());
+ final ActivityTaskManagerInternal.PreBindInfo preBindInfo =
+ mAtmInternal.preBindApplication(app.getWindowProcessController(), appInfo);
+ app.setCompat(preBindInfo.compatibilityInfo);
final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
if (mPlatformCompat != null) {
mPlatformCompat.resetReporting(app.info);
@@ -4716,7 +4712,7 @@
enableTrackAllocation,
isRestrictedBackupMode || !normalMode,
app.isPersistent(),
- new Configuration(app.getWindowProcessController().getConfiguration()),
+ preBindInfo.configuration,
app.getCompat(),
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
@@ -5426,7 +5422,9 @@
for (int i=0; i<intents.length; i++) {
Intent intent = intents[i];
if (intent != null) {
- intent.prepareToEnterSystemServer();
+ if (intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
if (type == ActivityManager.INTENT_SENDER_BROADCAST &&
(intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
throw new IllegalArgumentException(
@@ -5459,6 +5457,7 @@
}
}
intents[i] = new Intent(intent);
+ intents[i].removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
}
if (resolvedTypes != null && resolvedTypes.length != intents.length) {
@@ -13591,7 +13590,12 @@
enforceNotIsolatedCaller("startService");
enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
if (service != null) {
- service.prepareToEnterSystemServer();
+ // Refuse possible leaked file descriptors
+ if (service.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // Remove existing mismatch flag so it can be properly updated later
+ service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
if (callingPackage == null) {
@@ -13828,7 +13832,12 @@
enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
if (service != null) {
- service.prepareToEnterSystemServer();
+ // Refuse possible leaked file descriptors
+ if (service.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // Remove existing mismatch flag so it can be properly updated later
+ service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
if (callingPackage == null) {
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 1b00cec..6aadcdc 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -33,6 +33,7 @@
import android.content.pm.PackageManager;
import android.icu.text.SimpleDateFormat;
import android.os.Binder;
+import android.os.Debug;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
@@ -495,6 +496,10 @@
private void addBaseFieldsFromProcessRecord(ApplicationStartInfo start, ProcessRecord app) {
if (app == null) {
+ if (DEBUG) {
+ Slog.w(TAG,
+ "app is null in addBaseFieldsFromProcessRecord: " + Debug.getCallers(4));
+ }
return;
}
final int definingUid = app.getHostingRecord() != null
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8e87342..3f4902d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -123,31 +123,16 @@
import com.android.server.net.BaseNetworkObserver;
import com.android.server.pm.UserManagerInternal;
import com.android.server.power.optimization.Flags;
-import com.android.server.power.stats.AggregatedPowerStatsConfig;
-import com.android.server.power.stats.AmbientDisplayPowerStatsProcessor;
-import com.android.server.power.stats.AudioPowerStatsProcessor;
import com.android.server.power.stats.BatteryExternalStatsWorker;
import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
-import com.android.server.power.stats.BluetoothPowerStatsProcessor;
-import com.android.server.power.stats.CameraPowerStatsProcessor;
-import com.android.server.power.stats.CpuPowerStatsProcessor;
-import com.android.server.power.stats.CustomEnergyConsumerPowerStatsProcessor;
-import com.android.server.power.stats.FlashlightPowerStatsProcessor;
-import com.android.server.power.stats.GnssPowerStatsProcessor;
-import com.android.server.power.stats.MobileRadioPowerStatsProcessor;
-import com.android.server.power.stats.PhoneCallPowerStatsProcessor;
-import com.android.server.power.stats.PowerStatsAggregator;
-import com.android.server.power.stats.PowerStatsExporter;
+import com.android.server.power.stats.PowerAttributor;
import com.android.server.power.stats.PowerStatsScheduler;
import com.android.server.power.stats.PowerStatsStore;
import com.android.server.power.stats.PowerStatsUidResolver;
-import com.android.server.power.stats.ScreenPowerStatsProcessor;
-import com.android.server.power.stats.SensorPowerStatsProcessor;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
-import com.android.server.power.stats.VideoPowerStatsProcessor;
-import com.android.server.power.stats.WifiPowerStatsProcessor;
+import com.android.server.power.stats.processor.MultiStatePowerAttributor;
import com.android.server.power.stats.wakeups.CpuWakeupStats;
import java.io.File;
@@ -207,7 +192,7 @@
private final AtomicFile mConfigFile;
private final BatteryStats.BatteryStatsDumpHelper mDumpHelper;
private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
- private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+ private final PowerAttributor mPowerAttributor;
private volatile boolean mMonitorEnabled = true;
@@ -445,14 +430,12 @@
mStats.startTrackingSystemServerCpuTime();
}
- mAggregatedPowerStatsConfig = createAggregatedPowerStatsConfig();
- mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig);
+ mPowerStatsStore = new PowerStatsStore(systemDir, mHandler);
+ mPowerAttributor = new MultiStatePowerAttributor(mContext, mPowerStatsStore, mPowerProfile,
+ mCpuScalingPolicies, mPowerStatsUidResolver);
mPowerStatsScheduler = createPowerStatsScheduler(mContext);
- PowerStatsExporter powerStatsExporter =
- new PowerStatsExporter(mPowerStatsStore,
- new PowerStatsAggregator(mAggregatedPowerStatsConfig, mStats.getHistory()));
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
- powerStatsExporter, mPowerProfile, mCpuScalingPolicies,
+ mPowerAttributor, mPowerProfile, mCpuScalingPolicies,
mPowerStatsStore, Clock.SYSTEM_CLOCK);
mStats.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider, mPowerStatsStore);
mDumpHelper = new BatteryStatsDumpHelperImpl(mBatteryUsageStatsProvider);
@@ -472,154 +455,11 @@
onAlarmListener, aHandler);
};
return new PowerStatsScheduler(mStats::schedulePowerStatsSampleCollection,
- new PowerStatsAggregator(mAggregatedPowerStatsConfig,
- mStats.getHistory()), aggregatedPowerStatsSpanDuration,
+ mStats.getHistory(), mPowerAttributor, aggregatedPowerStatsSpanDuration,
powerStatsAggregationPeriod, mPowerStatsStore, alarmScheduler, Clock.SYSTEM_CLOCK,
mMonotonicClock, () -> mStats.getHistory().getStartTime(), mHandler);
}
- private AggregatedPowerStatsConfig createAggregatedPowerStatsConfig() {
- AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new CpuPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .setProcessorSupplier(
- () -> new ScreenPowerStatsProcessor(mPowerProfile));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
- BatteryConsumer.POWER_COMPONENT_SCREEN)
- .setProcessorSupplier(AmbientDisplayPowerStatsProcessor::new);
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new MobileRadioPowerStatsProcessor(mPowerProfile));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
- BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
- .setProcessorSupplier(PhoneCallPowerStatsProcessor::new);
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_WIFI)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new WifiPowerStatsProcessor(mPowerProfile));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new BluetoothPowerStatsProcessor(mPowerProfile));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AUDIO)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new AudioPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new VideoPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new FlashlightPowerStatsProcessor(mPowerProfile,
- mPowerStatsUidResolver));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new CameraPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(
- () -> new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
- config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessorSupplier(() -> new SensorPowerStatsProcessor(
- () -> mContext.getSystemService(SensorManager.class)));
-
- config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
- .trackDeviceStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(
- AggregatedPowerStatsConfig.STATE_POWER,
- AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
- return config;
- }
-
private void setPowerStatsThrottlePeriods(BatteryStatsImpl.BatteryStatsConfig.Builder builder,
String configString) {
if (configString == null) {
@@ -664,83 +504,84 @@
}
public void systemServicesReady() {
+ MultiStatePowerAttributor attributor = (MultiStatePowerAttributor) mPowerAttributor;
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CPU,
Flags.streamlinedBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_CPU,
Flags.streamlinedBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_SCREEN,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_SCREEN,
Flags.streamlinedMiscBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
Flags.streamlinedMiscBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
Flags.streamlinedConnectivityBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
Flags.streamlinedConnectivityBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_PHONE,
Flags.streamlinedConnectivityBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_WIFI,
Flags.streamlinedConnectivityBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_WIFI,
Flags.streamlinedConnectivityBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
Flags.streamlinedConnectivityBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
Flags.streamlinedConnectivityBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_AUDIO,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_AUDIO,
Flags.streamlinedMiscBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_VIDEO,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_VIDEO,
Flags.streamlinedMiscBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
Flags.streamlinedMiscBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_GNSS,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_GNSS,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_SENSORS,
Flags.streamlinedMiscBatteryStats());
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CAMERA,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_CAMERA,
Flags.streamlinedMiscBatteryStats());
// By convention POWER_COMPONENT_ANY represents custom Energy Consumers
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_ANY,
Flags.streamlinedMiscBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ attributor.setPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_ANY,
Flags.streamlinedMiscBatteryStats());
@@ -1281,7 +1122,11 @@
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
final byte[] statsProto = bus.getStatsProto();
-
+ try {
+ bus.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure close BatteryUsageStats", e);
+ }
data.add(FrameworkStatsLog.buildStatsEvent(atomTag, statsProto));
return StatsManager.PULL_SUCCESS;
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 32026b2..f7085b4 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -1808,7 +1808,12 @@
final Intent verifyBroadcastLocked(Intent intent) {
if (intent != null) {
- intent.prepareToEnterSystemServer();
+ // Refuse possible leaked file descriptors
+ if (intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // Remove existing mismatch flag so it can be properly updated later
+ intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
int flags = intent.getFlags();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 8fe33d1..9e4666c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1005,13 +1005,10 @@
final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();
- if ((info.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
- queue.setActiveWasStopped(true);
- }
- final int intentFlags = r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND;
- final boolean firstLaunch = !mService.wasPackageEverLaunched(info.packageName, r.userId);
- queue.setActiveFirstLaunch(firstLaunch);
+ queue.setActiveWasStopped(info.isStopped());
+ queue.setActiveFirstLaunch(info.isNotLaunched());
+ final int intentFlags = r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND;
final HostingRecord hostingRecord = new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST,
component, r.intent.getAction(), r.getHostingRecordTriggerType());
final boolean isActivityCapable = (r.options != null
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 8f52f67..416c110 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -86,7 +86,6 @@
import dalvik.annotation.optimization.NeverCompile;
-import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -2318,6 +2317,7 @@
Slog.d(TAG_AM, "Skipping freeze because process is marked "
+ "should not be frozen");
}
+ reportProcessFreezableChangedLocked(proc);
return;
}
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 1314521..da40826 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -554,13 +554,11 @@
callingProcessState, proc.mState.getCurProcState(),
false, 0L);
} else {
- final boolean stopped =
- (cpr.appInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0;
+ final boolean stopped = cpr.appInfo.isStopped();
final int packageState = stopped
? PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED
: PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
- final boolean firstLaunch = !mService.wasPackageEverLaunched(
- cpi.packageName, userId);
+ final boolean firstLaunch = cpr.appInfo.isNotLaunched();
checkTime(startTime, "getContentProviderImpl: before start process");
proc = mService.startProcessLocked(
cpi.processName, cpr.appInfo, false, 0,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 0e266f5..f0cc09f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -78,7 +78,6 @@
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
import static android.os.Process.setProcessGroup;
-import static android.os.Process.setThreadPriority;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
@@ -447,6 +446,19 @@
long getElapsedRealtimeMillis() {
return SystemClock.elapsedRealtime();
}
+
+ void batchSetOomAdj(ArrayList<ProcessRecord> procsToOomAdj) {
+ ProcessList.batchSetOomAdj(procsToOomAdj);
+ }
+
+ void setOomAdj(int pid, int uid, int adj) {
+ ProcessList.setOomAdj(pid, uid, adj);
+ }
+
+ void setThreadPriority(int tid, int priority) {
+ Process.setThreadPriority(tid, priority);
+ }
+
}
boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -1126,26 +1138,31 @@
final int numLru = lruList.size();
if (mConstants.USE_TIERED_CACHED_ADJ) {
final long now = mInjector.getUptimeMillis();
+ int uiTargetAdj = 10;
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
- if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
- >= UNKNOWN_ADJ) {
+ if (!app.isKilledByAm() && app.getThread() != null
+ && (state.getCurAdj() >= UNKNOWN_ADJ
+ || (state.hasShownUi() && state.getCurAdj() >= CACHED_APP_MIN_ADJ))) {
final ProcessServiceRecord psr = app.mServices;
int targetAdj = CACHED_APP_MIN_ADJ;
if (opt != null && opt.isFreezeExempt()) {
// BIND_WAIVE_PRIORITY and the like get oom_adj 900
targetAdj += 0;
+ } else if (state.hasShownUi() && uiTargetAdj < 15) {
+ // The most recent 5 apps that have shown UI get 910-914
+ targetAdj += uiTargetAdj++;
} else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ)
&& (state.getLastStateTime()
+ mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) {
// Older cached apps get 950
targetAdj += 50;
} else {
- // Newer cached apps get 910
- targetAdj += 10;
+ // Newer cached apps get 920
+ targetAdj += 20;
}
state.setCurRawAdj(targetAdj);
state.setCurAdj(psr.modifyRawOomAdj(targetAdj));
@@ -1431,7 +1448,7 @@
}
if (!mProcsToOomAdj.isEmpty()) {
- ProcessList.batchSetOomAdj(mProcsToOomAdj);
+ mInjector.batchSetOomAdj(mProcsToOomAdj);
mProcsToOomAdj.clear();
}
@@ -1906,7 +1923,6 @@
int procState;
int capability = cycleReEval ? getInitialCapability(app) : 0;
- boolean foregroundActivities = false;
boolean hasVisibleActivities = false;
if (app == topApp && PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP) {
// The last app on the list is the foreground app.
@@ -1920,7 +1936,6 @@
schedGroup = SCHED_GROUP_DEFAULT;
state.setAdjType("intermediate-top-activity");
}
- foregroundActivities = true;
hasVisibleActivities = true;
procState = PROCESS_STATE_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -1971,7 +1986,6 @@
adj = FOREGROUND_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
state.setAdjType("top-sleeping");
- foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app);
@@ -1991,7 +2005,8 @@
}
}
- // Examine all activities if not already foreground.
+ // Examine all non-top activities.
+ boolean foregroundActivities = app == topApp;
if (!foregroundActivities && state.getCachedHasActivities()) {
state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
adj, foregroundActivities, hasVisibleActivities, procState, schedGroup,
@@ -2515,25 +2530,6 @@
}
}
- state.setCurRawAdj(adj);
- adj = psr.modifyRawOomAdj(adj);
- if (adj > state.getMaxAdj()) {
- adj = state.getMaxAdj();
- if (adj <= PERCEPTIBLE_LOW_APP_ADJ) {
- schedGroup = SCHED_GROUP_DEFAULT;
- }
- }
-
- // Put bound foreground services in a special sched group for additional
- // restrictions on screen off
- if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
- && !state.shouldScheduleLikeTopApp()) {
- if (schedGroup > SCHED_GROUP_RESTRICTED) {
- schedGroup = SCHED_GROUP_RESTRICTED;
- }
- }
-
// apply capability from FGS.
if (psr.hasForegroundServices()) {
capability |= capabilityFromFGS;
@@ -3400,7 +3396,7 @@
if (isBatchingOomAdj && mConstants.ENABLE_BATCHING_OOM_ADJ) {
mProcsToOomAdj.add(app);
} else {
- ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
+ mInjector.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
}
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
@@ -3460,10 +3456,11 @@
ActivityManagerService.setFifoPriority(app, true /* enable */);
} else {
// Boost priority for top app UI and render threads
- setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
+ mInjector.setThreadPriority(app.getPid(),
+ THREAD_PRIORITY_TOP_APP_BOOST);
if (renderThreadTid != 0) {
try {
- setThreadPriority(renderThreadTid,
+ mInjector.setThreadPriority(renderThreadTid,
THREAD_PRIORITY_TOP_APP_BOOST);
} catch (IllegalArgumentException e) {
// thread died, ignore
@@ -3477,14 +3474,14 @@
if (app.useFifoUiScheduling()) {
// Reset UI pipeline to SCHED_OTHER
ActivityManagerService.setFifoPriority(app, false /* enable */);
- setThreadPriority(app.getPid(), state.getSavedPriority());
+ mInjector.setThreadPriority(app.getPid(), state.getSavedPriority());
} else {
// Reset priority for top app UI and render threads
- setThreadPriority(app.getPid(), 0);
+ mInjector.setThreadPriority(app.getPid(), 0);
}
if (renderThreadTid != 0) {
- setThreadPriority(renderThreadTid, THREAD_PRIORITY_DISPLAY);
+ mInjector.setThreadPriority(renderThreadTid, THREAD_PRIORITY_DISPLAY);
}
}
} catch (Exception e) {
@@ -3674,7 +3671,7 @@
if (app.useFifoUiScheduling()) {
mService.scheduleAsFifoPriority(app.getPid(), true);
} else {
- setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
+ mInjector.setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
}
if (isScreenOnOrAnimatingLocked(state)) {
initialSchedGroup = SCHED_GROUP_TOP_APP;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bb0c24b..4898f10 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2390,8 +2390,8 @@
}
String volumeUuid = packageState.getVolumeUuid();
long inode = packageState.getUserStateOrDefault(userId).getCeDataInode();
- if (inode == 0) {
- Slog.w(TAG, packageName + " inode == 0 (b/152760674)");
+ if (inode <= 0) {
+ Slog.w(TAG, packageName + " inode == 0 or app uninstalled with keep-data");
return null;
}
result.put(packageName, Pair.create(volumeUuid, inode));
@@ -3394,24 +3394,37 @@
hostingRecord.getDefiningUid(), hostingRecord.getDefiningProcessName());
final ProcessStateRecord state = r.mState;
- final boolean wasStopped = (info.flags & ApplicationInfo.FLAG_STOPPED) != 0;
+ final boolean wasStopped = info.isStopped();
// Check if we should mark the processrecord for first launch after force-stopping
if (wasStopped) {
+ boolean wasEverLaunched = false;
+ if (android.app.Flags.useAppInfoNotLaunched()) {
+ wasEverLaunched = !info.isNotLaunched();
+ } else {
+ try {
+ wasEverLaunched = mService.getPackageManagerInternal()
+ .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId);
+ } catch (IllegalArgumentException e) {
+ // Package doesn't have state yet, assume not launched
+ }
+ }
// Check if the hosting record is for an activity or not. Since the stopped
// state tracking is handled differently to avoid WM calling back into AM,
// store the state in the correct record
if (hostingRecord.isTypeActivity()) {
- final boolean wasPackageEverLaunched = mService
- .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId);
// If the package was launched in the past but is currently stopped, only then
// should it be considered as force-stopped.
- @WindowProcessController.StoppedState int stoppedState = wasPackageEverLaunched
+ @WindowProcessController.StoppedState int stoppedState = wasEverLaunched
? STOPPED_STATE_FORCE_STOPPED
: STOPPED_STATE_FIRST_LAUNCH;
r.getWindowProcessController().setStoppedState(stoppedState);
} else {
- r.setWasForceStopped(true);
- // first launch is computed just before logging, for non-activity types
+ if (android.app.Flags.useAppInfoNotLaunched()) {
+ // If it was launched before, then it must be a force-stop
+ r.setWasForceStopped(wasEverLaunched);
+ } else {
+ r.setWasForceStopped(true);
+ }
}
}
@@ -4112,19 +4125,6 @@
return false;
}
- private static int procStateToImportance(int procState, int memAdj,
- ActivityManager.RunningAppProcessInfo currApp,
- int clientTargetSdk) {
- int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
- procState, clientTargetSdk);
- if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
- currApp.lru = memAdj;
- } else {
- currApp.lru = 0;
- }
- return imp;
- }
-
@GuardedBy(anyOf = {"mService", "mProcLock"})
void fillInProcMemInfoLOSP(ProcessRecord app,
ActivityManager.RunningAppProcessInfo outInfo,
@@ -4142,14 +4142,20 @@
}
outInfo.lastTrimLevel = app.mProfile.getTrimMemoryLevel();
final ProcessStateRecord state = app.mState;
- int adj = state.getCurAdj();
- int procState = state.getCurProcState();
- outInfo.importance = procStateToImportance(procState, adj, outInfo,
- clientTargetSdk);
+ final int procState = state.getCurProcState();
+ outInfo.importance = ActivityManager.RunningAppProcessInfo
+ .procStateToImportanceForTargetSdk(procState, clientTargetSdk);
+ if (outInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+ outInfo.lru = state.getCurAdj();
+ } else {
+ outInfo.lru = 0;
+ }
outInfo.importanceReasonCode = state.getAdjTypeCode();
outInfo.processState = procState;
outInfo.isFocused = (app == mService.getTopApp());
outInfo.lastActivityTime = app.getLastActivityTime();
+ // Note: ActivityManager$RunningAppProcessInfo.copyTo() must be updated if what gets
+ // "filled into" outInfo in this method changes.
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 2937307..439bca0 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -139,6 +139,7 @@
static final String[] sDeviceConfigAconfigScopes = new String[] {
"accessibility",
"android_core_networking",
+ "android_health_services",
"android_sdk",
"android_stylus",
"aoc",
@@ -235,7 +236,6 @@
"wear_connectivity",
"wear_esim_carriers",
"wear_frameworks",
- "wear_health_services",
"wear_media",
"wear_offload",
"wear_security",
@@ -453,7 +453,9 @@
proto.write(StorageRequestMessage.FlagOverrideMessage.PACKAGE_NAME, packageName);
proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_NAME, flagName);
proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_VALUE, flagValue);
- proto.write(StorageRequestMessage.FlagOverrideMessage.IS_LOCAL, isLocal);
+ proto.write(StorageRequestMessage.FlagOverrideMessage.OVERRIDE_TYPE, isLocal
+ ? StorageRequestMessage.LOCAL_ON_REBOOT
+ : StorageRequestMessage.SERVER_ON_REBOOT);
proto.end(msgToken);
proto.end(msgsToken);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index bdba6bc..b186eaa 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1978,6 +1978,7 @@
boolean userSwitchUiEnabled;
synchronized (mLock) {
mCurrentUserId = userId;
+ ActivityManager.invalidateGetCurrentUserIdCache();
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
mInjector.updateUserConfiguration();
@@ -2239,6 +2240,7 @@
return true;
}
mTargetUserId = targetUserId;
+ ActivityManager.invalidateGetCurrentUserIdCache();
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
if (userSwitchUiEnabled) {
@@ -2316,6 +2318,7 @@
synchronized (mLock) {
nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL);
mTargetUserId = UserHandle.USER_NULL;
+ ActivityManager.invalidateGetCurrentUserIdCache();
}
if (nextUserId != UserHandle.USER_NULL) {
switchUser(nextUserId);
@@ -3021,6 +3024,9 @@
mInjector.getUserManagerInternal().addUserLifecycleListener(mUserLifecycleListener);
updateProfileRelatedCaches();
mInjector.reportCurWakefulnessUsageEvent();
+
+ // IpcDataCache must be invalidated before it starts caching.
+ ActivityManager.invalidateGetCurrentUserIdCache();
}
// TODO(b/266158156): remove this method if initial system user boot logic is refactored?
@@ -3184,6 +3190,9 @@
@GuardedBy("mLock")
private int getCurrentOrTargetUserIdLU() {
+ // Note: this result is currently cached by ActivityManager.getCurrentUser() - changes to
+ // the logic here may require updating how the cache is invalidated.
+ // See ActivityManager.invalidateGetCurrentUserIdCache() for more details.
return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 154b52b..6ae6f3d 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -33,7 +33,6 @@
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.app.AppOpsManager.OP_BLUETOOTH_CONNECT;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_CAMERA_SANDBOXED;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
@@ -3115,11 +3114,6 @@
packageName);
}
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as incoming "
- + "package: " + packageName + " and uid: " + uid + " is invalid");
- }
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3149,13 +3143,6 @@
}
} catch (SecurityException e) {
logVerifyAndGetBypassFailure(uid, e, "noteOperation");
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
- + " verifyAndGetBypass returned a SecurityException for package: "
- + packageName + " and uid: " + uid + " and attributionTag: "
- + attributionTag, e);
- }
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3173,17 +3160,6 @@
if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName + "flags: " +
AppOpsManager.flagsToString(flags));
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
- + " #getOpsLocked returned null for"
- + " uid: " + uid
- + " packageName: " + packageName
- + " attributionTag: " + attributionTag
- + " pvr.isAttributionTagValid: " + pvr.isAttributionTagValid
- + " pvr.bypass: " + pvr.bypass);
- Slog.e(TAG, "mUidStates.get(" + uid + "): " + mUidStates.get(uid));
- }
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3228,11 +3204,6 @@
attributedOp.rejected(uidState.getState(), flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag,
virtualDeviceId, flags, uidMode);
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT && uidMode == MODE_ERRORED) {
- Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
- + " uid mode is MODE_ERRORED");
- }
return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
}
} else {
@@ -3252,11 +3223,6 @@
attributedOp.rejected(uidState.getState(), flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag,
virtualDeviceId, flags, mode);
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT && mode == MODE_ERRORED) {
- Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
- + " package mode is MODE_ERRORED");
- }
return new SyncNotedAppOp(mode, code, attributionTag, packageName);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 168ec05..6daf0d0 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -285,7 +285,6 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CancellationException;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -411,6 +410,9 @@
/** The controller for the volume UI. */
private final VolumeController mVolumeController = new VolumeController();
+ /** Used only for testing to enable/disable the long press timeout volume actions. */
+ private final AtomicBoolean mVolumeControllerLongPressEnabled = new AtomicBoolean(true);
+
// sendMsg() flags
/** If the msg is already queued, replace it with this one. */
private static final int SENDMSG_REPLACE = 0;
@@ -790,6 +792,7 @@
private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
private final Executor mAudioServerLifecycleExecutor;
+ private long mSysPropListenerNativeHandle;
private final List<Future> mScheduledPermissionTasks = new ArrayList();
private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -10640,7 +10643,7 @@
}, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS));
}
};
- mAudioSystem.listenForSystemPropertyChange(
+ mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
PermissionManager.CACHE_KEY_PACKAGE_INFO,
task);
} else {
@@ -12553,6 +12556,15 @@
if (DEBUG_VOL) Log.d(TAG, "Volume controller visible: " + visible);
}
+ /** @see AudioManager#setVolumeControllerLongPressTimeoutEnabled(boolean) */
+ @Override
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public void setVolumeControllerLongPressTimeoutEnabled(boolean enable) {
+ super.setVolumeControllerLongPressTimeoutEnabled_enforcePermission();
+ mVolumeControllerLongPressEnabled.set(enable);
+ Log.i(TAG, "Volume controller long press timeout enabled: " + enable);
+ }
+
@Override
public void setVolumePolicy(VolumePolicy policy) {
enforceVolumeController("set volume policy");
@@ -12631,7 +12643,9 @@
if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) {
// UI is not visible yet, adjustment is ignored
if (mNextLongPress < now) {
- mNextLongPress = now + mLongPressTimeout;
+ mNextLongPress =
+ now + (mVolumeControllerLongPressEnabled.get() ? mLongPressTimeout
+ : 0);
}
suppress = true;
} else if (mNextLongPress > 0) { // in a long-press
@@ -12698,11 +12712,6 @@
if (mController == null)
return;
try {
- // TODO: remove this when deprecating STREAM_BLUETOOTH_SCO
- if (isStreamBluetoothSco(streamType)) {
- // TODO: notify both sco and voice_call about volume changes
- streamType = AudioSystem.STREAM_BLUETOOTH_SCO;
- }
mController.volumeChanged(streamType, flags);
} catch (RemoteException e) {
Log.w(TAG, "Error calling volumeChanged", e);
@@ -14713,6 +14722,8 @@
@Override
/** @see AudioManager#permissionUpdateBarrier() */
public void permissionUpdateBarrier() {
+ if (!audioserverPermissions()) return;
+ mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
List<Future> snapshot;
synchronized (mScheduledPermissionTasks) {
snapshot = List.copyOf(mScheduledPermissionTasks);
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index d083c68..5cabdde 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -748,8 +748,12 @@
return AudioSystem.setMasterMute(mute);
}
- public void listenForSystemPropertyChange(String systemPropertyName, Runnable callback) {
- AudioSystem.listenForSystemPropertyChange(systemPropertyName, callback);
+ public long listenForSystemPropertyChange(String systemPropertyName, Runnable callback) {
+ return AudioSystem.listenForSystemPropertyChange(systemPropertyName, callback);
+ }
+
+ public void triggerSystemPropertyUpdate(long handle) {
+ AudioSystem.triggerSystemPropertyUpdate(handle);
}
/**
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 70f3193..7e26356 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -1302,7 +1302,7 @@
mEventLogger.enqueue((new EventLogger.StringEvent(
"abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+ "/" + Binder.getCallingPid()
- + " clientId=" + clientId))
+ + " clientId=" + clientId + " callingPack=" + callingPackageName))
.printLog(TAG));
try {
// this will take care of notifying the new focus owner if needed
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 2a16872..5d85089 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -50,7 +50,6 @@
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.biometrics.face.IFace;
-import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -74,6 +73,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemService;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import java.util.ArrayList;
@@ -203,7 +203,7 @@
*/
@VisibleForTesting
public String[] getFingerprintAidlInstances() {
- return ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+ return FingerprintService.getDeclaredInstances();
}
/**
@@ -850,10 +850,28 @@
return;
}
+ boolean tempResetLockoutRequiresChallenge = false;
+
+ if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
+ for (String configString : hidlConfigStrings) {
+ try {
+ SensorConfig sensor = new SensorConfig(configString);
+ switch (sensor.modality) {
+ case BiometricAuthenticator.TYPE_FACE:
+ tempResetLockoutRequiresChallenge = true;
+ break;
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error parsing configString: " + configString, e);
+ }
+ }
+ }
+
+ final boolean resetLockoutRequiresChallenge = tempResetLockoutRequiresChallenge;
+
handlerProvider.getFaceHandler().post(() -> {
final FaceSensorConfigurations mFaceSensorConfigurations =
- new FaceSensorConfigurations(hidlConfigStrings != null
- && hidlConfigStrings.length > 0);
+ new FaceSensorConfigurations(resetLockoutRequiresChallenge);
if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
mFaceSensorConfigurations.addHidlConfigs(hidlConfigStrings, context);
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index eaf4f81..b2c616a 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -120,7 +120,9 @@
userId), trustManager)) {
isMandatoryBiometricsAuthentication = true;
promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
- promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
+ if (promptInfo.getNegativeButtonText() == null) {
+ promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
+ }
}
final boolean biometricRequested = Utils.isBiometricRequested(promptInfo);
@@ -314,6 +316,7 @@
Pair<BiometricSensor, Integer> sensorNotEnrolled = null;
Pair<BiometricSensor, Integer> sensorLockout = null;
Pair<BiometricSensor, Integer> hardwareNotDetected = null;
+ Pair<BiometricSensor, Integer> biometricAppNotAllowed = null;
for (Pair<BiometricSensor, Integer> pair : ineligibleSensors) {
final int status = pair.second;
if (status == BIOMETRIC_LOCKOUT_TIMED || status == BIOMETRIC_LOCKOUT_PERMANENT) {
@@ -325,6 +328,9 @@
if (status == BIOMETRIC_HARDWARE_NOT_DETECTED) {
hardwareNotDetected = pair;
}
+ if (status == BIOMETRIC_NOT_ENABLED_FOR_APPS) {
+ biometricAppNotAllowed = pair;
+ }
}
// If there is a sensor locked out, prioritize lockout over other sensor's error.
@@ -337,6 +343,10 @@
return hardwareNotDetected;
}
+ if (Flags.mandatoryBiometrics() && biometricAppNotAllowed != null) {
+ return biometricAppNotAllowed;
+ }
+
// If the caller requested STRONG, and the device contains both STRONG and non-STRONG
// sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's
// BIOMETRIC_INSUFFICIENT_STRENGTH error.
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 8711214..407ef1e 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -321,6 +321,9 @@
case BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE:
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
break;
+ case BiometricConstants.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS:
+ biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS;
+ break;
default:
Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
@@ -384,9 +387,12 @@
return BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED;
case MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR:
return BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
+ case BIOMETRIC_NOT_ENABLED_FOR_APPS:
+ if (Flags.mandatoryBiometrics()) {
+ return BiometricConstants.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS;
+ }
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
case BIOMETRIC_HARDWARE_NOT_DETECTED:
- case BIOMETRIC_NOT_ENABLED_FOR_APPS:
default:
return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index f5af5ea..bc58501 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -78,7 +78,6 @@
public void authenticate(OperationContextExt operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
int authState, boolean requireConfirmation, int targetUserId, float ambientLightLux) {
- Slog.d(TAG, "authenticate logging " + operationContext.getIsMandatoryBiometrics());
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
statsModality,
targetUserId,
@@ -131,7 +130,6 @@
public void error(OperationContextExt operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
int error, int vendorCode, int targetUserId) {
- Slog.d(TAG, "error logging " + operationContext.getIsMandatoryBiometrics());
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
statsModality,
targetUserId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 60cfd5a..2f6ba0b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -26,6 +26,7 @@
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.fingerprint.FingerprintSensorConfigurations.getIFingerprint;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -78,6 +79,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
@@ -1015,7 +1017,7 @@
this(context, BiometricContext.getInstance(context),
() -> IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
- () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR),
+ () -> getDeclaredInstances(),
null /* fingerprintProvider */,
null /* fingerprintProviderFunction */);
}
@@ -1039,8 +1041,7 @@
mFingerprintProvider = fingerprintProvider != null ? fingerprintProvider :
(name) -> {
final String fqName = IFingerprint.DESCRIPTOR + "/" + name;
- final IFingerprint fp = IFingerprint.Stub.asInterface(
- Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+ final IFingerprint fp = getIFingerprint(fqName);
if (fp != null) {
try {
return new FingerprintProvider(getContext(),
@@ -1129,6 +1130,24 @@
publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper);
}
+ /**
+ * Get all fingerprint hal instances declared in manifest
+ * @return instance names
+ */
+ public static String[] getDeclaredInstances() {
+ String[] a = ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+ Slog.i(TAG, "Before:getDeclaredInstances: IFingerprint instance found, a.length="
+ + a.length);
+ if (!ArrayUtils.contains(a, "virtual")) {
+ // Now, the virtual hal is registered with IVirtualHal interface and it is also
+ // moved from vendor to system_ext partition without a device manifest. So
+ // if the old vhal is not declared, add here.
+ a = ArrayUtils.appendElement(String.class, a, "virtual");
+ }
+ Slog.i(TAG, "After:getDeclaredInstances: a.length=" + a.length);
+ return a;
+ }
+
@NonNull
private List<Fingerprint> getEnrolledFingerprintsDeprecated(int userId, String opPackageName) {
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index caa2c1c..fd3d996 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -20,9 +20,9 @@
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode;
-import android.hardware.biometrics.fingerprint.EnrollmentProgressStep;
-import android.hardware.biometrics.fingerprint.NextEnrollment;
+import android.hardware.biometrics.fingerprint.virtualhal.AcquiredInfoAndVendorCode;
+import android.hardware.biometrics.fingerprint.virtualhal.EnrollmentProgressStep;
+import android.hardware.biometrics.fingerprint.virtualhal.NextEnrollment;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
@@ -300,4 +300,4 @@
super.getSensorId_enforcePermission();
return mSensorId;
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 9edaa4e..8195efe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -17,6 +17,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY;
+import static android.hardware.fingerprint.FingerprintSensorConfigurations.getIFingerprint;
+import static android.hardware.fingerprint.FingerprintSensorConfigurations.remapFqName;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,9 +36,9 @@
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.fingerprint.IFingerprint;
-import android.hardware.biometrics.fingerprint.IVirtualHal;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.biometrics.fingerprint.virtualhal.IVirtualHal;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintEnrollOptions;
@@ -291,7 +293,8 @@
if (mTestHalEnabled) {
return true;
}
- return (ServiceManager.checkService(IFingerprint.DESCRIPTOR + "/" + mHalInstanceName)
+ return (ServiceManager.checkService(
+ remapFqName(IFingerprint.DESCRIPTOR + "/" + mHalInstanceName))
!= null);
}
@@ -330,10 +333,7 @@
Slog.d(getTag(), "Daemon was null, reconnecting");
- mDaemon = IFingerprint.Stub.asInterface(
- Binder.allowBlocking(
- ServiceManager.waitForDeclaredService(
- IFingerprint.DESCRIPTOR + "/" + mHalInstanceNameCurrent)));
+ mDaemon = getIFingerprint(IFingerprint.DESCRIPTOR + "/" + mHalInstanceNameCurrent);
if (mDaemon == null) {
Slog.e(getTag(), "Unable to get daemon");
return null;
@@ -905,8 +905,8 @@
void setTestHalEnabled(boolean enabled) {
final boolean changed = enabled != mTestHalEnabled;
mTestHalEnabled = enabled;
- Slog.i(getTag(), "setTestHalEnabled(): useVhalForTesting=" + Flags.useVhalForTesting()
- + "mTestHalEnabled=" + mTestHalEnabled + " changed=" + changed);
+ Slog.i(getTag(), "setTestHalEnabled(): useVhalForTestingFlags=" + Flags.useVhalForTesting()
+ + " mTestHalEnabled=" + mTestHalEnabled + " changed=" + changed);
if (changed && useVhalForTesting()) {
getHalInstance();
}
@@ -999,12 +999,13 @@
*/
public IVirtualHal getVhal() throws RemoteException {
if (mVhal == null && useVhalForTesting()) {
- mVhal = IVirtualHal.Stub.asInterface(mDaemon.asBinder().getExtension());
- if (mVhal == null) {
- Slog.e(getTag(), "Unable to get fingerprint virtualhal interface");
- }
+ mVhal = IVirtualHal.Stub.asInterface(
+ Binder.allowBlocking(
+ ServiceManager.waitForService(
+ IVirtualHal.DESCRIPTOR + "/"
+ + mHalInstanceNameCurrent)));
+ Slog.d(getTag(), "getVhal " + mHalInstanceNameCurrent);
}
-
return mVhal;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index d12d7b2..25d1fe7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static android.hardware.fingerprint.FingerprintSensorConfigurations.remapFqName;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -356,8 +358,8 @@
if (mTestHalEnabled) {
return true;
}
- return (ServiceManager.checkService(IFingerprint.DESCRIPTOR + "/" + halInstance)
- != null);
+ return (ServiceManager.checkService(
+ remapFqName(IFingerprint.DESCRIPTOR + "/" + halInstance)) != null);
}
@NonNull protected BiometricContext getBiometricContext() {
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 028b9b0..fcbcb02 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -32,8 +32,9 @@
public BroadcastRadioService(Context context) {
super(context);
ArrayList<String> serviceNameList = IRadioServiceAidlImpl.getServicesNames();
- mServiceImpl = serviceNameList.isEmpty() ? new IRadioServiceHidlImpl(this)
- : new IRadioServiceAidlImpl(this, serviceNameList);
+ RadioServiceUserController userController = new RadioServiceUserControllerImpl();
+ mServiceImpl = serviceNameList.isEmpty() ? new IRadioServiceHidlImpl(this, userController)
+ : new IRadioServiceAidlImpl(this, serviceNameList, userController);
}
@Override
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
index 16514fa..332958d 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
@@ -69,8 +69,9 @@
return serviceList;
}
- IRadioServiceAidlImpl(BroadcastRadioService service, ArrayList<String> serviceList) {
- this(service, new BroadcastRadioServiceImpl(serviceList));
+ IRadioServiceAidlImpl(BroadcastRadioService service, List<String> serviceList,
+ RadioServiceUserController userController) {
+ this(service, new BroadcastRadioServiceImpl(serviceList, userController));
Slogf.i(TAG, "Initialize BroadcastRadioServiceAidl(%s)", service);
}
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index ab08342..67d3c95 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -59,13 +59,16 @@
@GuardedBy("mLock")
private final List<RadioManager.ModuleProperties> mV1Modules;
- IRadioServiceHidlImpl(BroadcastRadioService service) {
+ IRadioServiceHidlImpl(BroadcastRadioService service,
+ RadioServiceUserController userController) {
mService = Objects.requireNonNull(service, "broadcast radio service cannot be null");
- mHal1Client = new com.android.server.broadcastradio.hal1.BroadcastRadioService();
+ Objects.requireNonNull(userController, "user controller cannot be null");
+ mHal1Client = new com.android.server.broadcastradio.hal1.BroadcastRadioService(
+ userController);
mV1Modules = mHal1Client.loadModules();
OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
mHal2Client = new com.android.server.broadcastradio.hal2.BroadcastRadioService(
- max.isPresent() ? max.getAsInt() + 1 : 0);
+ max.isPresent() ? max.getAsInt() + 1 : 0, userController);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java b/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
index c705ebe..c15ccf1 100644
--- a/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
+++ b/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
@@ -16,19 +16,11 @@
package com.android.server.broadcastradio;
-import android.app.ActivityManager;
-import android.os.Binder;
-import android.os.UserHandle;
-
/**
- * Controller to handle users in {@link com.android.server.broadcastradio.BroadcastRadioService}
+ * Controller interface to handle users in
+ * {@link com.android.server.broadcastradio.BroadcastRadioService}
*/
-public final class RadioServiceUserController {
-
- private RadioServiceUserController() {
- throw new UnsupportedOperationException(
- "RadioServiceUserController class is noninstantiable");
- }
+public interface RadioServiceUserController {
/**
* Check if the user calling the method in Broadcast Radio Service is the current user or the
@@ -37,26 +29,20 @@
* @return {@code true} if the user calling this method is the current user of system user,
* {@code false} otherwise.
*/
- public static boolean isCurrentOrSystemUser() {
- int callingUser = Binder.getCallingUserHandle().getIdentifier();
- return callingUser == getCurrentUser() || callingUser == UserHandle.USER_SYSTEM;
- }
+ boolean isCurrentOrSystemUser();
/**
* Get current foreground user for Broadcast Radio Service
*
* @return foreground user id.
*/
- public static int getCurrentUser() {
- int userId = UserHandle.USER_NULL;
- final long identity = Binder.clearCallingIdentity();
- try {
- userId = ActivityManager.getCurrentUser();
- } catch (RuntimeException e) {
- // Activity manager not running, nothing we can do assume user 0.
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return userId;
- }
-}
+ int getCurrentUser();
+
+ /**
+ * Get id of the user handle assigned to the process that sent the binder transaction that is
+ * being processed
+ *
+ * @return Id of the user handle
+ */
+ int getCallingUserId();
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/broadcastradio/RadioServiceUserControllerImpl.java b/services/core/java/com/android/server/broadcastradio/RadioServiceUserControllerImpl.java
new file mode 100644
index 0000000..e305d20
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/RadioServiceUserControllerImpl.java
@@ -0,0 +1,62 @@
+/**
+ * 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.server.broadcastradio;
+
+import android.app.ActivityManager;
+import android.os.Binder;
+import android.os.UserHandle;
+
+/**
+ * Implementation for the controller to handle users in
+ * {@link com.android.server.broadcastradio.BroadcastRadioService}
+ */
+public final class RadioServiceUserControllerImpl implements RadioServiceUserController {
+
+ /**
+ * @see RadioServiceUserController#isCurrentOrSystemUser()
+ */
+ @Override
+ public boolean isCurrentOrSystemUser() {
+ int callingUser = getCallingUserId();
+ return callingUser == getCurrentUser() || callingUser == UserHandle.USER_SYSTEM;
+ }
+
+ /**
+ * @see RadioServiceUserController#getCurrentUser()
+ */
+ @Override
+ public int getCurrentUser() {
+ int userId = UserHandle.USER_NULL;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ userId = ActivityManager.getCurrentUser();
+ } catch (RuntimeException e) {
+ // Activity manager not running, nothing we can do assume user 0.
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return userId;
+ }
+
+ /**
+ * @see RadioServiceUserController#getCallingUserId()
+ */
+ @Override
+ public int getCallingUserId() {
+ return Binder.getCallingUserHandle().getIdentifier();
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index 7b50465..06024b5 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -51,6 +51,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Object mLock = new Object();
+ private final RadioServiceUserController mUserController;
@GuardedBy("mLock")
private int mNextModuleId;
@@ -77,7 +78,7 @@
}
RadioModule radioModule =
- RadioModule.tryLoadingModule(moduleId, name, newBinder);
+ RadioModule.tryLoadingModule(moduleId, name, newBinder, mUserController);
if (radioModule == null) {
Slogf.w(TAG, "No module %s with id %d (HAL AIDL)", name, moduleId);
return;
@@ -141,9 +142,12 @@
* BroadcastRadio HAL services
*
* @param serviceNameList list of names of AIDL BroadcastRadio HAL services
+ * @param userController User controller implementation
*/
- public BroadcastRadioServiceImpl(ArrayList<String> serviceNameList) {
+ public BroadcastRadioServiceImpl(List<String> serviceNameList,
+ RadioServiceUserController userController) {
mNextModuleId = 0;
+ mUserController = Objects.requireNonNull(userController, "User controller can not be null");
if (DEBUG) {
Slogf.d(TAG, "Initializing BroadcastRadioServiceImpl %s", IBroadcastRadio.DESCRIPTOR);
}
@@ -202,7 +206,7 @@
if (DEBUG) {
Slogf.d(TAG, "Open AIDL radio session");
}
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.e(TAG, "Cannot open tuner on AIDL HAL client for non-current user");
throw new IllegalStateException("Cannot open session for non-current user");
}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index a176a32..20ee49e 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -62,6 +62,7 @@
private final Handler mHandler;
private final RadioEventLogger mLogger;
private final RadioManager.ModuleProperties mProperties;
+ private final RadioServiceUserController mUserController;
/**
* Tracks antenna state reported by HAL (if any).
@@ -194,15 +195,18 @@
};
@VisibleForTesting
- RadioModule(IBroadcastRadio service, RadioManager.ModuleProperties properties) {
+ RadioModule(IBroadcastRadio service, RadioManager.ModuleProperties properties,
+ RadioServiceUserController userController) {
mProperties = Objects.requireNonNull(properties, "properties cannot be null");
mService = Objects.requireNonNull(service, "service cannot be null");
mHandler = new Handler(Looper.getMainLooper());
+ mUserController = Objects.requireNonNull(userController, "User controller can not be null");
mLogger = new RadioEventLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
}
@Nullable
- static RadioModule tryLoadingModule(int moduleId, String moduleName, IBinder serviceBinder) {
+ static RadioModule tryLoadingModule(int moduleId, String moduleName, IBinder serviceBinder,
+ RadioServiceUserController userController) {
try {
Slogf.i(TAG, "Try loading module for module id = %d, module name = %s",
moduleId, moduleName);
@@ -232,7 +236,7 @@
RadioManager.ModuleProperties prop = ConversionUtils.propertiesFromHalProperties(
moduleId, moduleName, service.getProperties(), amfmConfig, dabConfig);
- return new RadioModule(service, prop);
+ return new RadioModule(service, prop, userController);
} catch (RemoteException ex) {
Slogf.e(TAG, ex, "Failed to load module %s", moduleName);
return null;
@@ -256,7 +260,7 @@
RadioManager.ProgramInfo currentProgramInfo;
synchronized (mLock) {
boolean isFirstTunerSession = mAidlTunerSessions.isEmpty();
- tunerSession = new TunerSession(this, mService, userCb);
+ tunerSession = new TunerSession(this, mService, userCb, mUserController);
mAidlTunerSessions.add(tunerSession);
antennaConnected = mAntennaConnected;
currentProgramInfo = mCurrentProgramInfo;
@@ -440,7 +444,7 @@
@GuardedBy("mLock")
private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
- int currentUserId = RadioServiceUserController.getCurrentUser();
+ int currentUserId = mUserController.getCurrentUser();
List<TunerSession> deadSessions = null;
for (int i = 0; i < mAidlTunerSessions.size(); i++) {
if (mAidlTunerSessions.valueAt(i).mUserId != currentUserId
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index e90a1dd..f22661b 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -52,6 +52,7 @@
final android.hardware.radio.ITunerCallback mCallback;
private final int mUid;
private final IBroadcastRadio mService;
+ private final RadioServiceUserController mUserController;
@GuardedBy("mLock")
private boolean mIsClosed;
@@ -65,11 +66,13 @@
private RadioManager.BandConfig mPlaceHolderConfig;
TunerSession(RadioModule radioModule, IBroadcastRadio service,
- android.hardware.radio.ITunerCallback callback) {
+ android.hardware.radio.ITunerCallback callback,
+ RadioServiceUserController userController) {
mModule = Objects.requireNonNull(radioModule, "radioModule cannot be null");
mService = Objects.requireNonNull(service, "service cannot be null");
- mUserId = Binder.getCallingUserHandle().getIdentifier();
mCallback = Objects.requireNonNull(callback, "callback cannot be null");
+ mUserController = Objects.requireNonNull(userController, "User controller can not be null");
+ mUserId = mUserController.getCallingUserId();
mUid = Binder.getCallingUid();
mLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
}
@@ -126,7 +129,7 @@
@Override
public void setConfiguration(RadioManager.BandConfig config) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot set configuration for AIDL HAL client from non-current user");
return;
}
@@ -169,7 +172,7 @@
public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
mLogger.logRadioEvent("Step with direction %s, skipSubChannel? %s",
directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot step on AIDL HAL client from non-current user");
return;
}
@@ -187,7 +190,7 @@
public void seek(boolean directionDown, boolean skipSubChannel) throws RemoteException {
mLogger.logRadioEvent("Seek with direction %s, skipSubChannel? %s",
directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot scan on AIDL HAL client from non-current user");
return;
}
@@ -204,7 +207,7 @@
@Override
public void tune(ProgramSelector selector) throws RemoteException {
mLogger.logRadioEvent("Tune with selector %s", selector);
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot tune on AIDL HAL client from non-current user");
return;
}
@@ -226,7 +229,7 @@
@Override
public void cancel() {
Slogf.i(TAG, "Cancel");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot cancel on AIDL HAL client from non-current user");
return;
}
@@ -255,7 +258,7 @@
@Override
public boolean startBackgroundScan() {
Slogf.w(TAG, "Explicit background scan trigger is not supported with HAL AIDL");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot start background scan on AIDL HAL client from non-current user");
return false;
}
@@ -268,7 +271,7 @@
@Override
public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
mLogger.logRadioEvent("Start programList updates %s", filter);
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG,
"Cannot start program list updates on AIDL HAL client from non-current user");
return;
@@ -344,7 +347,7 @@
@Override
public void stopProgramListUpdates() throws RemoteException {
mLogger.logRadioEvent("Stop programList updates");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG,
"Cannot stop program list updates on AIDL HAL client from non-current user");
return;
@@ -389,7 +392,7 @@
public void setConfigFlag(int flag, boolean value) throws RemoteException {
mLogger.logRadioEvent("set ConfigFlag %s to %b ",
ConfigFlag.$.toString(flag), value);
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot set config flag for AIDL HAL client from non-current user");
return;
}
@@ -406,7 +409,7 @@
@Override
public Map<String, String> setParameters(Map<String, String> parameters) {
mLogger.logRadioEvent("Set parameters ");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot set parameters for AIDL HAL client from non-current user");
return new ArrayMap<>();
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
index fb42c94..6a6a3ae 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
@@ -35,6 +35,7 @@
* This field is used by native code, do not access or modify.
*/
private final long mNativeContext = nativeInit();
+ private final RadioServiceUserController mUserController;
private final Object mLock = new Object();
@@ -50,6 +51,10 @@
private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
+ public BroadcastRadioService(RadioServiceUserController userController) {
+ mUserController = Objects.requireNonNull(userController, "User controller can not be null");
+ }
+
public @NonNull List<RadioManager.ModuleProperties> loadModules() {
synchronized (mLock) {
return Objects.requireNonNull(nativeLoadModules(mNativeContext));
@@ -58,7 +63,7 @@
public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
boolean withAudio, ITunerCallback callback) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.e(TAG, "Cannot open tuner on HAL 1.x client for non-current user");
throw new IllegalStateException("Cannot open tuner for non-current user");
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index 7cac409..8e64600d2 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
@@ -28,6 +28,7 @@
import android.os.RemoteException;
import com.android.server.broadcastradio.RadioServiceUserController;
+import com.android.server.broadcastradio.RadioServiceUserControllerImpl;
import com.android.server.utils.Slogf;
import java.util.List;
@@ -51,6 +52,7 @@
private boolean mIsMuted = false;
private int mRegion;
private final boolean mWithAudio;
+ private final RadioServiceUserController mUserController = new RadioServiceUserControllerImpl();
Tuner(@NonNull ITunerCallback clientCallback, int halRev,
int region, boolean withAudio, int band) {
@@ -127,7 +129,7 @@
@Override
public void setConfiguration(RadioManager.BandConfig config) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot set configuration for HAL 1.x client from non-current user");
return;
}
@@ -176,7 +178,7 @@
@Override
public void step(boolean directionDown, boolean skipSubChannel) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot step on HAL 1.x client from non-current user");
return;
}
@@ -189,7 +191,7 @@
@Override
public void seek(boolean directionDown, boolean skipSubChannel) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot seek on HAL 1.x client from non-current user");
return;
}
@@ -202,7 +204,7 @@
@Override
public void tune(ProgramSelector selector) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot tune on HAL 1.x client from non-current user");
return;
}
@@ -219,7 +221,7 @@
@Override
public void cancel() {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot cancel on HAL 1.x client from non-current user");
return;
}
@@ -231,7 +233,7 @@
@Override
public void cancelAnnouncement() {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot cancel announcement on HAL 1.x client from non-current user");
return;
}
@@ -260,7 +262,7 @@
@Override
public boolean startBackgroundScan() {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG,
"Cannot start background scan on HAL 1.x client from non-current user");
return false;
@@ -285,7 +287,7 @@
@Override
public void startProgramListUpdates(ProgramList.Filter filter) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG,
"Cannot start program list updates on HAL 1.x client from non-current user");
return;
@@ -295,7 +297,7 @@
@Override
public void stopProgramListUpdates() {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG,
"Cannot stop program list updates on HAL 1.x client from non-current user");
return;
@@ -321,7 +323,7 @@
@Override
public void setConfigFlag(int flag, boolean value) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot set config flag for HAL 1.x client from non-current user");
return;
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index a4efa2e..3227afd 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -50,6 +50,8 @@
private final Object mLock = new Object();
+ private final RadioServiceUserController mUserController;
+
@GuardedBy("mLock")
private int mNextModuleId;
@@ -75,7 +77,8 @@
moduleId = mNextModuleId;
}
- RadioModule radioModule = RadioModule.tryLoadingModule(moduleId, serviceName);
+ RadioModule radioModule = RadioModule.tryLoadingModule(moduleId, serviceName,
+ mUserController);
if (radioModule == null) {
return;
}
@@ -123,8 +126,9 @@
}
};
- public BroadcastRadioService(int nextModuleId) {
+ public BroadcastRadioService(int nextModuleId, RadioServiceUserController userController) {
mNextModuleId = nextModuleId;
+ mUserController = Objects.requireNonNull(userController, "User controller can not be null");
try {
IServiceManager manager = IServiceManager.getService();
if (manager == null) {
@@ -138,8 +142,10 @@
}
@VisibleForTesting
- BroadcastRadioService(int nextModuleId, IServiceManager manager) {
+ BroadcastRadioService(int nextModuleId, IServiceManager manager,
+ RadioServiceUserController userController) {
mNextModuleId = nextModuleId;
+ mUserController = Objects.requireNonNull(userController, "User controller can not be null");
Objects.requireNonNull(manager, "Service manager cannot be null");
try {
manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener);
@@ -171,7 +177,7 @@
public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
Slogf.v(TAG, "Open HIDL 2.0 session with module id " + moduleId);
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.e(TAG, "Cannot open tuner on HAL 2.0 client for non-current user");
throw new IllegalStateException("Cannot open session for non-current user");
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index d3b2448..a0d6cc2 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -66,6 +66,7 @@
private final Object mLock = new Object();
private final Handler mHandler;
private final RadioEventLogger mEventLogger;
+ private final RadioServiceUserController mUserController;
@GuardedBy("mLock")
private ITunerSession mHalTunerSession;
@@ -148,16 +149,18 @@
private final Set<TunerSession> mAidlTunerSessions = new ArraySet<>();
@VisibleForTesting
- RadioModule(@NonNull IBroadcastRadio service,
- @NonNull RadioManager.ModuleProperties properties) {
+ RadioModule(IBroadcastRadio service, RadioManager.ModuleProperties properties,
+ RadioServiceUserController userController) {
mProperties = Objects.requireNonNull(properties);
mService = Objects.requireNonNull(service);
mHandler = new Handler(Looper.getMainLooper());
+ mUserController = Objects.requireNonNull(userController, "User controller can not be null");
mEventLogger = new RadioEventLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
}
@Nullable
- static RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
+ static RadioModule tryLoadingModule(int idx, String fqName,
+ RadioServiceUserController controller) {
try {
Slogf.i(TAG, "Try loading module for idx " + idx + ", fqName " + fqName);
IBroadcastRadio service = IBroadcastRadio.getService(fqName);
@@ -179,7 +182,7 @@
RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName,
service.getProperties(), amfmConfig.value, dabConfig.value);
- return new RadioModule(service, prop);
+ return new RadioModule(service, prop, controller);
} catch (RemoteException ex) {
Slogf.e(TAG, "Failed to load module " + fqName, ex);
return null;
@@ -208,7 +211,8 @@
});
mHalTunerSession = Objects.requireNonNull(hwSession.value);
}
- TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb);
+ TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb,
+ mUserController);
mAidlTunerSessions.add(tunerSession);
// Propagate state to new client. Note: These callbacks are invoked while holding mLock
@@ -375,7 +379,7 @@
@GuardedBy("mLock")
private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
- int currentUserId = RadioServiceUserController.getCurrentUser();
+ int currentUserId = mUserController.getCurrentUser();
List<TunerSession> deadSessions = null;
for (TunerSession tunerSession : mAidlTunerSessions) {
if (tunerSession.mUserId != currentUserId && tunerSession.mUserId
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 80efacd..dc164b1 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -16,7 +16,6 @@
package com.android.server.broadcastradio.hal2;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.hardware.broadcastradio.V2_0.ConfigFlag;
@@ -27,7 +26,6 @@
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
-import android.os.Binder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -55,6 +53,7 @@
private final ITunerSession mHwSession;
final int mUserId;
final android.hardware.radio.ITunerCallback mCallback;
+ private final RadioServiceUserController mUserController;
@GuardedBy("mLock")
private boolean mIsClosed = false;
@@ -66,12 +65,14 @@
// necessary only for older APIs compatibility
private RadioManager.BandConfig mDummyConfig = null;
- TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession,
- @NonNull android.hardware.radio.ITunerCallback callback) {
+ TunerSession(RadioModule module, ITunerSession hwSession,
+ android.hardware.radio.ITunerCallback callback,
+ RadioServiceUserController userController) {
mModule = Objects.requireNonNull(module);
mHwSession = Objects.requireNonNull(hwSession);
- mUserId = Binder.getCallingUserHandle().getIdentifier();
mCallback = Objects.requireNonNull(callback);
+ mUserController = Objects.requireNonNull(userController, "User controller can not be null");
+ mUserId = mUserController.getCallingUserId();
mEventLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
}
@@ -120,7 +121,7 @@
@Override
public void setConfiguration(RadioManager.BandConfig config) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot set configuration for HAL 2.0 client from non-current user");
return;
}
@@ -162,7 +163,7 @@
public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
mEventLogger.logRadioEvent("Step with direction %s, skipSubChannel? %s",
directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot step on HAL 2.0 client from non-current user");
return;
}
@@ -177,7 +178,7 @@
public void seek(boolean directionDown, boolean skipSubChannel) throws RemoteException {
mEventLogger.logRadioEvent("Seek with direction %s, skipSubChannel? %s",
directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot scan on HAL 2.0 client from non-current user");
return;
}
@@ -191,7 +192,7 @@
@Override
public void tune(ProgramSelector selector) throws RemoteException {
mEventLogger.logRadioEvent("Tune with selector %s", selector);
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot tune on HAL 2.0 client from non-current user");
return;
}
@@ -205,7 +206,7 @@
@Override
public void cancel() {
Slogf.i(TAG, "Cancel");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot cancel on HAL 2.0 client from non-current user");
return;
}
@@ -230,7 +231,7 @@
@Override
public boolean startBackgroundScan() {
Slogf.w(TAG, "Explicit background scan trigger is not supported with HAL 2.0");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG,
"Cannot start background scan on HAL 2.0 client from non-current user");
return false;
@@ -242,7 +243,7 @@
@Override
public void startProgramListUpdates(ProgramList.Filter filter) {
mEventLogger.logRadioEvent("start programList updates %s", filter);
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG,
"Cannot start program list updates on HAL 2.0 client from non-current user");
return;
@@ -306,7 +307,7 @@
@Override
public void stopProgramListUpdates() throws RemoteException {
mEventLogger.logRadioEvent("Stop programList updates");
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG,
"Cannot stop program list updates on HAL 2.0 client from non-current user");
return;
@@ -355,7 +356,7 @@
@Override
public void setConfigFlag(int flag, boolean value) throws RemoteException {
mEventLogger.logRadioEvent("Set ConfigFlag %s = %b", ConfigFlag.toString(flag), value);
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot set config flag for HAL 2.0 client from non-current user");
return;
}
@@ -368,7 +369,7 @@
@Override
public Map<String, String> setParameters(Map<String, String> parameters) {
- if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+ if (!mUserController.isCurrentOrSystemUser()) {
Slogf.w(TAG, "Cannot set parameters for HAL 2.0 client from non-current user");
return new ArrayMap<>();
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 17835b2..05fc6bc 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -379,12 +379,8 @@
streamCount = mStreamStats.size();
}
if (CameraServiceProxy.DEBUG) {
- String ultrawideDebug = Flags.logUltrawideUsage()
- ? ", wideAngleUsage " + mUsedUltraWide
- : "";
- String zoomOverrideDebug = Flags.logZoomOverrideUsage()
- ? ", zoomOverrideUsage " + mUsedZoomOverride
- : "";
+ String ultrawideDebug = ", wideAngleUsage " + mUsedUltraWide;
+ String zoomOverrideDebug = ", zoomOverrideUsage " + mUsedZoomOverride;
String mostRequestedFpsRangeDebug = Flags.analytics24q3()
? ", mostRequestedFpsRange " + mMostRequestedFpsRange
: "";
@@ -1338,9 +1334,8 @@
List<CameraStreamStats> streamStats = cameraState.getStreamStats();
String userTag = cameraState.getUserTag();
int videoStabilizationMode = cameraState.getVideoStabilizationMode();
- boolean usedUltraWide = Flags.logUltrawideUsage() ? cameraState.getUsedUltraWide() : false;
- boolean usedZoomOverride =
- Flags.logZoomOverrideUsage() ? cameraState.getUsedZoomOverride() : false;
+ boolean usedUltraWide = cameraState.getUsedUltraWide();
+ boolean usedZoomOverride = cameraState.getUsedZoomOverride();
long logId = cameraState.getLogId();
int sessionIdx = cameraState.getSessionIndex();
CameraExtensionSessionStats extSessionStats = cameraState.getExtensionSessionStats();
diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java b/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
new file mode 100644
index 0000000..3eb3380
--- /dev/null
+++ b/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.server.crashrecovery;
+
+import android.os.Environment;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+
+/**
+ * Class containing helper methods for the CrashRecoveryModule.
+ *
+ * @hide
+ */
+public class CrashRecoveryUtils {
+ private static final String TAG = "CrashRecoveryUtils";
+ private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 1000 * 1000; // ~1MB
+ private static final Object sFileLock = new Object();
+
+ /** Persist recovery related events in crashrecovery events file.**/
+ public static void logCrashRecoveryEvent(int priority, String msg) {
+ Slog.println(priority, TAG, msg);
+ try {
+ File fname = getCrashRecoveryEventsFile();
+ synchronized (sFileLock) {
+ FileOutputStream out = new FileOutputStream(fname, true);
+ PrintWriter pw = new PrintWriter(out);
+ String dateString = LocalDateTime.now(ZoneId.systemDefault()).toString();
+ pw.println(dateString + ": " + msg);
+ pw.close();
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to log CrashRecoveryEvents " + e.getMessage());
+ }
+ }
+
+ /** Dump recovery related events from crashrecovery events file.**/
+ public static void dumpCrashRecoveryEvents(IndentingPrintWriter pw) {
+ pw.println("CrashRecovery Events: ");
+ pw.increaseIndent();
+ final File file = getCrashRecoveryEventsFile();
+ final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE;
+ synchronized (sFileLock) {
+ try (BufferedReader in = new BufferedReader(new FileReader(file))) {
+ if (skipSize > 0) {
+ in.skip(skipSize);
+ }
+ String line;
+ while ((line = in.readLine()) != null) {
+ pw.println(line);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to dump CrashRecoveryEvents " + e.getMessage());
+ }
+ }
+ pw.decreaseIndent();
+ }
+
+ private static File getCrashRecoveryEventsFile() {
+ File systemDir = new File(Environment.getDataDirectory(), "system");
+ return new File(systemDir, "crashrecovery-events.txt");
+ }
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 240e91b..86015ac 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -45,6 +45,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -562,6 +563,7 @@
public void resetShortTermModel() {
mCurrentBrightnessMapper.clearUserDataPoints();
mShortTermModel.reset();
+ Slog.i(TAG, "Resetting short term model");
}
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
@@ -598,74 +600,79 @@
}
public void dump(PrintWriter pw) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.increaseIndent();
pw.println();
pw.println("Automatic Brightness Controller Configuration:");
- pw.println(" mState=" + configStateToString(mState));
- pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
- pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
- pw.println(" mDozeScaleFactor=" + mDozeScaleFactor);
- pw.println(" mInitialLightSensorRate=" + mInitialLightSensorRate);
- pw.println(" mNormalLightSensorRate=" + mNormalLightSensorRate);
- pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
- pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
- pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
- pw.println(" mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
- pw.println(" mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
- pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
- pw.println(" mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
- pw.println(" mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
- pw.println(" mWeightingIntercept=" + mWeightingIntercept);
+ pw.println("----------------------------------------------");
+ ipw.println("mState=" + configStateToString(mState));
+ ipw.println("mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
+ ipw.println("mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
+ ipw.println("mDozeScaleFactor=" + mDozeScaleFactor);
+ ipw.println("mInitialLightSensorRate=" + mInitialLightSensorRate);
+ ipw.println("mNormalLightSensorRate=" + mNormalLightSensorRate);
+ ipw.println("mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
+ ipw.println("mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
+ ipw.println("mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
+ ipw.println("mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
+ ipw.println("mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
+ ipw.println("mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
+ ipw.println("mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
+ ipw.println("mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
+ ipw.println("mWeightingIntercept=" + mWeightingIntercept);
pw.println();
pw.println("Automatic Brightness Controller State:");
- pw.println(" mLightSensor=" + mLightSensor);
- pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
- pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
- pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate);
- pw.println(" mAmbientLux=" + mAmbientLux);
- pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
- pw.println(" mPreThresholdLux=" + mPreThresholdLux);
- pw.println(" mPreThresholdBrightness=" + mPreThresholdBrightness);
- pw.println(" mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
- pw.println(" mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
- pw.println(" mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
- pw.println(" mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
- pw.println(" mLastObservedLux=" + mLastObservedLux);
- pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
- pw.println(" mRecentLightSamples=" + mRecentLightSamples);
- pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
- pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
- pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
- pw.println(" mShortTermModel=");
- mShortTermModel.dump(pw);
- pw.println(" mPausedShortTermModel=");
- mPausedShortTermModel.dump(pw);
+ pw.println("--------------------------------------");
+ ipw.println("mLightSensor=" + mLightSensor);
+ ipw.println("mLightSensorEnabled=" + mLightSensorEnabled);
+ ipw.println("mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
+ ipw.println("mCurrentLightSensorRate=" + mCurrentLightSensorRate);
+ ipw.println("mAmbientLux=" + mAmbientLux);
+ ipw.println("mAmbientLuxValid=" + mAmbientLuxValid);
+ ipw.println("mPreThresholdLux=" + mPreThresholdLux);
+ ipw.println("mPreThresholdBrightness=" + mPreThresholdBrightness);
+ ipw.println("mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
+ ipw.println("mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
+ ipw.println("mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
+ ipw.println("mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
+ ipw.println("mLastObservedLux=" + mLastObservedLux);
+ ipw.println("mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
+ ipw.println("mRecentLightSamples=" + mRecentLightSamples);
+ ipw.println("mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
+ ipw.println("mScreenAutoBrightness=" + mScreenAutoBrightness);
+ ipw.println("mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
+ ipw.println("mShortTermModel=");
- pw.println();
- pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
- pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
- pw.println(" mBrightnessAdjustmentSampleOldBrightness="
+ mShortTermModel.dump(ipw);
+ ipw.println("mPausedShortTermModel=");
+ mPausedShortTermModel.dump(ipw);
+
+ ipw.println();
+ ipw.println("mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
+ ipw.println("mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
+ ipw.println("mBrightnessAdjustmentSampleOldBrightness="
+ mBrightnessAdjustmentSampleOldBrightness);
- pw.println(" mForegroundAppPackageName=" + mForegroundAppPackageName);
- pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
- pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
- pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
- pw.println(" Current mode="
+ ipw.println("mForegroundAppPackageName=" + mForegroundAppPackageName);
+ ipw.println("mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+ ipw.println("mForegroundAppCategory=" + mForegroundAppCategory);
+ ipw.println("mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
+ ipw.println("Current mode="
+ autoBrightnessModeToString(mCurrentBrightnessMapper.getMode()));
for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) {
- pw.println();
- pw.println(" Mapper for mode "
+ ipw.println();
+ ipw.println("Mapper for mode "
+ autoBrightnessModeToString(mBrightnessMappingStrategyMap.keyAt(i)) + ":");
- mBrightnessMappingStrategyMap.valueAt(i).dump(pw,
+ mBrightnessMappingStrategyMap.valueAt(i).dump(ipw,
mBrightnessRangeController.getNormalBrightnessMax());
}
- pw.println();
- pw.println(" mAmbientBrightnessThresholds=" + mAmbientBrightnessThresholds);
- pw.println(" mAmbientBrightnessThresholdsIdle=" + mAmbientBrightnessThresholdsIdle);
- pw.println(" mScreenBrightnessThresholds=" + mScreenBrightnessThresholds);
- pw.println(" mScreenBrightnessThresholdsIdle=" + mScreenBrightnessThresholdsIdle);
+ ipw.println();
+ ipw.println("mAmbientBrightnessThresholds=" + mAmbientBrightnessThresholds);
+ ipw.println("mAmbientBrightnessThresholdsIdle=" + mAmbientBrightnessThresholdsIdle);
+ ipw.println("mScreenBrightnessThresholds=" + mScreenBrightnessThresholds);
+ ipw.println("mScreenBrightnessThresholdsIdle=" + mScreenBrightnessThresholdsIdle);
}
public float[] getLastSensorValues() {
@@ -931,7 +938,7 @@
setAmbientLux(mFastAmbientLux);
if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: "
- + ((mFastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+ + ((mFastAmbientLux > mPreThresholdLux) ? "Brightened" : "Darkened") + ": "
+ "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+ "mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold + ", "
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
@@ -1339,8 +1346,10 @@
+ "\n mIsValid: " + mIsValid;
}
- void dump(PrintWriter pw) {
- pw.println(this);
+ void dump(IndentingPrintWriter ipw) {
+ ipw.increaseIndent();
+ ipw.println(this);
+ ipw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index b0507fb..6a019f3 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -1073,7 +1073,9 @@
pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
pw.println(" shortTermModelTimeout=" + getShortTermModelTimeout());
- pw.println(" Previous short-term models (oldest to newest): ");
+ if (!mPreviousBrightnessSplines.isEmpty()) {
+ pw.println(" Previous short-term models (oldest to newest): ");
+ }
for (int i = 0; i < mPreviousBrightnessSplines.size(); i++) {
pw.println(" Computed at "
+ FORMAT.format(new Date(mBrightnessSplineChangeTimes.get(i))) + ": ");
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 8a3e392..1d68ee54 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import android.annotation.Nullable;
import android.hardware.display.BrightnessInfo;
import android.os.Handler;
import android.os.IBinder;
@@ -92,7 +93,7 @@
return mHbmController.getNormalBrightnessMax();
}
- void loadFromConfig(HighBrightnessModeMetadata hbmMetadata, IBinder token,
+ void loadFromConfig(@Nullable HighBrightnessModeMetadata hbmMetadata, IBinder token,
DisplayDeviceInfo info, DisplayDeviceConfig displayDeviceConfig) {
applyChanges(
() -> mNormalBrightnessModeController.resetNbmData(
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index 631e751..b56a234 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -265,6 +265,7 @@
private void dumpLocal(PrintWriter pw) {
pw.println("BrightnessThrottler:");
+ pw.println("--------------------");
pw.println(" mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
pw.println(" mThermalThrottlingData=" + mThermalThrottlingData);
pw.println(" mUniqueDisplayId=" + mUniqueDisplayId);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index ac5dd20..0f65360 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -782,6 +782,7 @@
public void dump(final PrintWriter pw) {
pw.println("BrightnessTracker state:");
+ pw.println("------------------------");
synchronized (mDataCollectionLock) {
pw.println(" mStarted=" + mStarted);
pw.println(" mLightSensor=" + mLightSensor);
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 146810f..4c133ff 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -95,6 +95,7 @@
public void dumpLocked(IndentingPrintWriter ipw) {
ipw.println("DeviceStateToLayoutMap:");
+ ipw.println("-----------------------");
ipw.increaseIndent();
ipw.println("mIsPortInDisplayLayoutEnabled=" + mIsPortInDisplayLayoutEnabled);
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index dc611fc..01bbd2f 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import android.hardware.display.BrightnessInfo;
+import android.os.PowerManager;
import android.text.TextUtils;
import com.android.server.display.brightness.BrightnessEvent;
@@ -50,6 +52,8 @@
private final boolean mIsUserInitiatedChange;
+ private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason;
+
private DisplayBrightnessState(Builder builder) {
mBrightness = builder.getBrightness();
mHdrBrightness = builder.getHdrBrightness();
@@ -64,6 +68,7 @@
mBrightnessEvent = builder.getBrightnessEvent();
mBrightnessAdjustmentFlag = builder.getBrightnessAdjustmentFlag();
mIsUserInitiatedChange = builder.isUserInitiatedChange();
+ mBrightnessMaxReason = builder.getBrightnessMaxReason();
}
/**
@@ -159,6 +164,13 @@
return mIsUserInitiatedChange;
}
+ /**
+ * Gets reason for max brightness restriction
+ */
+ public @BrightnessInfo.BrightnessMaxReason int getBrightnessMaxReason() {
+ return mBrightnessMaxReason;
+ }
+
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -180,6 +192,8 @@
.append(Objects.toString(mBrightnessEvent, "null"));
stringBuilder.append("\n mBrightnessAdjustmentFlag:").append(mBrightnessAdjustmentFlag);
stringBuilder.append("\n mIsUserInitiatedChange:").append(mIsUserInitiatedChange);
+ stringBuilder.append("\n mBrightnessMaxReason:")
+ .append(BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
return stringBuilder.toString();
}
@@ -212,7 +226,8 @@
== otherState.shouldUpdateScreenBrightnessSetting()
&& Objects.equals(mBrightnessEvent, otherState.getBrightnessEvent())
&& mBrightnessAdjustmentFlag == otherState.getBrightnessAdjustmentFlag()
- && mIsUserInitiatedChange == otherState.isUserInitiatedChange();
+ && mIsUserInitiatedChange == otherState.isUserInitiatedChange()
+ && mBrightnessMaxReason == otherState.getBrightnessMaxReason();
}
@Override
@@ -221,7 +236,7 @@
mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mMinBrightness,
mCustomAnimationRate,
mShouldUpdateScreenBrightnessSetting, mBrightnessEvent, mBrightnessAdjustmentFlag,
- mIsUserInitiatedChange);
+ mIsUserInitiatedChange, mBrightnessMaxReason);
}
/**
@@ -241,16 +256,15 @@
private String mDisplayBrightnessStrategyName;
private boolean mShouldUseAutoBrightness;
private boolean mIsSlowChange;
- private float mMaxBrightness;
+ private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
private float mMinBrightness;
private float mCustomAnimationRate = CUSTOM_ANIMATION_RATE_NOT_SET;
private boolean mShouldUpdateScreenBrightnessSetting;
-
private BrightnessEvent mBrightnessEvent;
-
- public int mBrightnessAdjustmentFlag = 0;
-
+ private int mBrightnessAdjustmentFlag = 0;
private boolean mIsUserInitiatedChange;
+ private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason =
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
/**
* Create a builder starting with the values from the specified {@link
@@ -274,6 +288,7 @@
builder.setBrightnessEvent(state.getBrightnessEvent());
builder.setBrightnessAdjustmentFlag(state.getBrightnessAdjustmentFlag());
builder.setIsUserInitiatedChange(state.isUserInitiatedChange());
+ builder.setBrightnessMaxReason(state.getBrightnessMaxReason());
return builder;
}
@@ -506,5 +521,21 @@
mIsUserInitiatedChange = isUserInitiatedChange;
return this;
}
+
+ /**
+ * Gets reason for max brightness restriction
+ */
+ public @BrightnessInfo.BrightnessMaxReason int getBrightnessMaxReason() {
+ return mBrightnessMaxReason;
+ }
+
+ /**
+ * Sets reason for max brightness restriction
+ */
+ public Builder setBrightnessMaxReason(
+ @BrightnessInfo.BrightnessMaxReason int brightnessMaxReason) {
+ mBrightnessMaxReason = brightnessMaxReason;
+ return this;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index cc115f1..d78fdfa 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -150,7 +150,9 @@
* <screenBrightnessDefault>0.65</screenBrightnessDefault>
* <powerThrottlingConfig>
* <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>
- * <pollingWindowMillis>15</pollingWindowMillis>
+ * <customAnimationRateSec>0.004</customAnimationRateSec>
+ * <pollingWindowMaxMillis>30000</pollingWindowMaxMillis>
+ * <pollingWindowMinMillis>10000</pollingWindowMinMillis>
* <powerThrottlingMap>
* <powerThrottlingPoint>
* <thermalStatus>severe</thermalStatus>
@@ -2184,9 +2186,13 @@
return;
}
float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue();
- int pollingWindowMillis = powerThrottlingCfg.getPollingWindowMillis().intValue();
+ float customAnimationRateSec = powerThrottlingCfg.getCustomAnimationRateSec().floatValue();
+ int pollingWindowMaxMillis = powerThrottlingCfg.getPollingWindowMaxMillis().intValue();
+ int pollingWindowMinMillis = powerThrottlingCfg.getPollingWindowMinMillis().intValue();
mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap,
- pollingWindowMillis);
+ customAnimationRateSec,
+ pollingWindowMaxMillis,
+ pollingWindowMinMillis);
}
private void loadRefreshRateSetting(DisplayConfiguration config) {
@@ -2980,12 +2986,19 @@
public static class PowerThrottlingConfigData {
/** Lowest brightness cap allowed for this device. */
public final float brightnessLowestCapAllowed;
- /** Time window for polling power in seconds. */
- public final int pollingWindowMillis;
+ /** Time take to animate brightness in seconds. */
+ public final float customAnimationRateSec;
+ /** Time window for maximum polling power in milliseconds. */
+ public final int pollingWindowMaxMillis;
+ /** Time window for minimum polling power in milliseconds. */
+ public final int pollingWindowMinMillis;
public PowerThrottlingConfigData(float brightnessLowestCapAllowed,
- int pollingWindowMillis) {
+ float customAnimationRateSec, int pollingWindowMaxMillis,
+ int pollingWindowMinMillis) {
this.brightnessLowestCapAllowed = brightnessLowestCapAllowed;
- this.pollingWindowMillis = pollingWindowMillis;
+ this.customAnimationRateSec = customAnimationRateSec;
+ this.pollingWindowMaxMillis = pollingWindowMaxMillis;
+ this.pollingWindowMinMillis = pollingWindowMinMillis;
}
@Override
@@ -2993,7 +3006,9 @@
return "PowerThrottlingConfigData{"
+ "brightnessLowestCapAllowed: "
+ brightnessLowestCapAllowed
- + ", pollingWindowMillis: " + pollingWindowMillis
+ + ", customAnimationRateSec: " + customAnimationRateSec
+ + ", pollingWindowMaxMillis: " + pollingWindowMaxMillis
+ + ", pollingWindowMinMillis: " + pollingWindowMinMillis
+ "} ";
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 187caba..9644b1d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -134,6 +134,7 @@
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
+import android.util.MathUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -269,6 +270,7 @@
private final Context mContext;
private final DisplayManagerHandler mHandler;
+ private final HandlerExecutor mHandlerExecutor;
private final Handler mUiHandler;
private final DisplayModeDirector mDisplayModeDirector;
private final ExternalDisplayPolicy mExternalDisplayPolicy;
@@ -315,6 +317,7 @@
public boolean mSafeMode;
// All callback records indexed by calling process id.
+ @GuardedBy("mSyncRoot")
private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
/**
@@ -354,7 +357,7 @@
new CopyOnWriteArrayList<>();
/** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
- private final SparseArray<DisplayPowerControllerInterface> mDisplayPowerControllers =
+ private final SparseArray<DisplayPowerController> mDisplayPowerControllers =
new SparseArray<>();
/** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */
@@ -602,6 +605,7 @@
mContext = context;
mFlags = injector.getFlags();
mHandler = new DisplayManagerHandler(displayThreadLooper);
+ mHandlerExecutor = new HandlerExecutor(mHandler);
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
@@ -726,7 +730,7 @@
if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
return;
}
- final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
+ final DisplayPowerController dpc = mDisplayPowerControllers.get(
logicalDisplay.getDisplayIdLocked());
if (dpc == null) {
return;
@@ -760,12 +764,13 @@
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+
ActivityManager activityManager = mContext.getSystemService(ActivityManager.class);
activityManager.addOnUidImportanceListener(mUidImportanceListener, IMPORTANCE_CACHED);
mDeviceStateManager = LocalServices.getService(DeviceStateManagerInternal.class);
mContext.getSystemService(DeviceStateManager.class).registerCallback(
- new HandlerExecutor(mHandler), new DeviceStateListener());
+ mHandlerExecutor, new DeviceStateListener());
mLogicalDisplayMapper.onWindowManagerReady();
scheduleTraversalLocked(false);
@@ -1019,6 +1024,10 @@
private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
@Override
public void onUidImportance(int uid, int importance) {
+ onUidImportanceInternal(uid, importance);
+ }
+
+ private void onUidImportanceInternal(int uid, int importance) {
synchronized (mPendingCallbackSelfLocked) {
if (importance >= IMPORTANCE_GONE) {
// Clean up as the app is already gone
@@ -1267,6 +1276,9 @@
|| isUidPresentOnDisplayInternal(callingUid, displayId)) {
return info;
}
+ } else if (displayId == Display.DEFAULT_DISPLAY) {
+ Slog.e(TAG, "Default display is null for info request from uid "
+ + callingUid);
}
return null;
}
@@ -2058,7 +2070,7 @@
configurePreferredDisplayModeLocked(display);
}
- DisplayPowerControllerInterface dpc = addDisplayPowerControllerLocked(display);
+ DisplayPowerController dpc = addDisplayPowerControllerLocked(display);
if (dpc != null) {
final int leadDisplayId = display.getLeadDisplayIdLocked();
updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
@@ -2067,7 +2079,7 @@
// that the follower display was added before the lead display.
mLogicalDisplayMapper.forEachLocked(d -> {
if (d.getLeadDisplayIdLocked() == displayId) {
- DisplayPowerControllerInterface followerDpc =
+ DisplayPowerController followerDpc =
mDisplayPowerControllers.get(d.getDisplayIdLocked());
if (followerDpc != null) {
updateDisplayPowerControllerLeaderLocked(followerDpc, displayId);
@@ -2151,21 +2163,19 @@
scheduleTraversalLocked(false);
mPersistentDataStore.saveIfNeeded();
- DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
final int leadDisplayId = display.getLeadDisplayIdLocked();
updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
HighBrightnessModeMetadata hbmMetadata =
mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
- if (hbmMetadata != null) {
- dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
- }
+ dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
}
}
private void updateDisplayPowerControllerLeaderLocked(
- @NonNull DisplayPowerControllerInterface dpc, int leadDisplayId) {
+ @NonNull DisplayPowerController dpc, int leadDisplayId) {
if (dpc.getLeadDisplayId() == leadDisplayId) {
// Lead display hasn't changed, nothing to do.
return;
@@ -2174,7 +2184,7 @@
// If it has changed, then we need to unregister from the previous leader if there was one.
final int prevLeaderId = dpc.getLeadDisplayId();
if (prevLeaderId != Layout.NO_LEAD_DISPLAY) {
- final DisplayPowerControllerInterface prevLeader =
+ final DisplayPowerController prevLeader =
mDisplayPowerControllers.get(prevLeaderId);
if (prevLeader != null) {
prevLeader.removeDisplayBrightnessFollower(dpc);
@@ -2183,7 +2193,7 @@
// And then, if it's following, register it with the new one.
if (leadDisplayId != Layout.NO_LEAD_DISPLAY) {
- final DisplayPowerControllerInterface newLeader =
+ final DisplayPowerController newLeader =
mDisplayPowerControllers.get(leadDisplayId);
if (newLeader != null) {
newLeader.addDisplayBrightnessFollower(dpc);
@@ -2215,16 +2225,17 @@
if (display.isValidLocked()) {
applyDisplayChangedLocked(display);
}
- return;
+ } else {
+ releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
}
- releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ Slog.i(TAG, "Logical display removed: " + display.getDisplayIdLocked());
}
private void releaseDisplayAndEmitEvent(LogicalDisplay display, int event) {
final int displayId = display.getDisplayIdLocked();
- final DisplayPowerControllerInterface dpc =
+ final DisplayPowerController dpc =
mDisplayPowerControllers.removeReturnOld(displayId);
if (dpc != null) {
updateDisplayPowerControllerLeaderLocked(dpc, Layout.NO_LEAD_DISPLAY);
@@ -2271,16 +2282,14 @@
private void handleLogicalDisplayDeviceStateTransitionLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
- final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+ final DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
final int leadDisplayId = display.getLeadDisplayIdLocked();
updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
HighBrightnessModeMetadata hbmMetadata =
mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
- if (hbmMetadata != null) {
- dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
- }
+ dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
}
}
@@ -2692,14 +2701,14 @@
if (userId != mCurrentUserId) {
return;
}
- DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
+ DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
if (dpc != null) {
dpc.setBrightnessConfiguration(c, /* shouldResetShortTermModel= */ true);
}
}
}
- private DisplayPowerControllerInterface getDpcFromUniqueIdLocked(String uniqueId) {
+ private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) {
final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice);
if (logicalDisplay != null) {
@@ -2740,7 +2749,7 @@
final BrightnessConfiguration config =
getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial);
if (config != null) {
- final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
+ final DisplayPowerController dpc = mDisplayPowerControllers.get(
logicalDisplay.getDisplayIdLocked());
if (dpc != null) {
dpc.setBrightnessConfiguration(config,
@@ -2987,7 +2996,7 @@
void setAutoBrightnessLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
- final DisplayPowerControllerInterface displayPowerController =
+ final DisplayPowerController displayPowerController =
mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setAutoBrightnessLoggingEnabled(enabled);
@@ -2997,7 +3006,7 @@
void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
- final DisplayPowerControllerInterface displayPowerController =
+ final DisplayPowerController displayPowerController =
mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
@@ -3023,7 +3032,7 @@
void setAmbientColorTemperatureOverride(float cct) {
synchronized (mSyncRoot) {
- final DisplayPowerControllerInterface displayPowerController =
+ final DisplayPowerController displayPowerController =
mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setAmbientColorTemperatureOverride(cct);
@@ -3033,7 +3042,7 @@
void setDockedAndIdleEnabled(boolean enabled, int displayId) {
synchronized (mSyncRoot) {
- final DisplayPowerControllerInterface displayPowerController =
+ final DisplayPowerController displayPowerController =
mDisplayPowerControllers.get(displayId);
if (displayPowerController != null) {
displayPowerController.setAutomaticScreenBrightnessMode(enabled
@@ -3090,6 +3099,7 @@
/**
* Get internal or external viewport. Create it if does not currently exist.
+ *
* @param viewportType - either INTERNAL or EXTERNAL
* @return the viewport with the requested type
*/
@@ -3232,36 +3242,40 @@
// After releasing the lock, send the notifications out.
for (int i = 0; i < mTempCallbacks.size(); i++) {
CallbackRecord callbackRecord = mTempCallbacks.get(i);
- final int uid = callbackRecord.mUid;
- final int pid = callbackRecord.mPid;
- if (isUidCached(uid)) {
- // For cached apps, save the pending event until it becomes non-cached
- synchronized (mPendingCallbackSelfLocked) {
- SparseArray<PendingCallback> pendingCallbacks = mPendingCallbackSelfLocked.get(
- uid);
- if (extraLogging(callbackRecord.mPackageName)) {
- Slog.i(TAG, "Uid is cached: " + uid
- + ", pendingCallbacks: " + pendingCallbacks);
- }
- if (pendingCallbacks == null) {
- pendingCallbacks = new SparseArray<>();
- mPendingCallbackSelfLocked.put(uid, pendingCallbacks);
- }
- PendingCallback pendingCallback = pendingCallbacks.get(pid);
- if (pendingCallback == null) {
- pendingCallbacks.put(pid,
- new PendingCallback(callbackRecord, displayId, event));
- } else {
- pendingCallback.addDisplayEvent(displayId, event);
- }
- }
- } else {
- callbackRecord.notifyDisplayEventAsync(displayId, event);
- }
+ deliverEventInternal(callbackRecord, displayId, event);
}
mTempCallbacks.clear();
}
+ private void deliverEventInternal(CallbackRecord callbackRecord, int displayId, int event) {
+ final int uid = callbackRecord.mUid;
+ final int pid = callbackRecord.mPid;
+ if (isUidCached(uid)) {
+ // For cached apps, save the pending event until it becomes non-cached
+ synchronized (mPendingCallbackSelfLocked) {
+ SparseArray<PendingCallback> pendingCallbacks = mPendingCallbackSelfLocked.get(
+ uid);
+ if (extraLogging(callbackRecord.mPackageName)) {
+ Slog.i(TAG, "Uid is cached: " + uid
+ + ", pendingCallbacks: " + pendingCallbacks);
+ }
+ if (pendingCallbacks == null) {
+ pendingCallbacks = new SparseArray<>();
+ mPendingCallbackSelfLocked.put(uid, pendingCallbacks);
+ }
+ PendingCallback pendingCallback = pendingCallbacks.get(pid);
+ if (pendingCallback == null) {
+ pendingCallbacks.put(pid,
+ new PendingCallback(callbackRecord, displayId, event));
+ } else {
+ pendingCallback.addDisplayEvent(displayId, event);
+ }
+ }
+ } else {
+ callbackRecord.notifyDisplayEventAsync(displayId, event);
+ }
+ }
+
private boolean extraLogging(String packageName) {
return mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(packageName);
}
@@ -3340,6 +3354,7 @@
pw.println();
final int displayStateCount = mDisplayStates.size();
pw.println("Display States: size=" + displayStateCount);
+ pw.println("---------------------");
for (int i = 0; i < displayStateCount; i++) {
final int displayId = mDisplayStates.keyAt(i);
final int displayState = mDisplayStates.valueAt(i);
@@ -3355,6 +3370,7 @@
pw.println();
pw.println("Display Adapters: size=" + mDisplayAdapters.size());
+ pw.println("------------------------");
for (DisplayAdapter adapter : mDisplayAdapters) {
pw.println(" " + adapter.getName());
adapter.dumpLocked(ipw);
@@ -3362,6 +3378,7 @@
pw.println();
pw.println("Display Devices: size=" + mDisplayDeviceRepo.sizeLocked());
+ pw.println("-----------------------");
mDisplayDeviceRepo.forEachLocked(device -> {
pw.println(" " + device.getDisplayDeviceInfoLocked());
device.dumpLocked(ipw);
@@ -3373,6 +3390,7 @@
final int callbackCount = mCallbacks.size();
pw.println();
pw.println("Callbacks: size=" + callbackCount);
+ pw.println("-----------------");
for (int i = 0; i < callbackCount; i++) {
CallbackRecord callback = mCallbacks.valueAt(i);
pw.println(" " + i + ": mPid=" + callback.mPid
@@ -3385,6 +3403,7 @@
for (int i = 0; i < displayPowerControllerCount; i++) {
mDisplayPowerControllers.valueAt(i).dump(pw);
}
+
pw.println();
mPersistentDataStore.dump(pw);
@@ -3403,8 +3422,10 @@
}
pw.println();
mDisplayModeDirector.dump(pw);
+ pw.println();
mBrightnessSynchronizer.dump(pw);
if (mSmallAreaDetectionController != null) {
+ pw.println();
mSmallAreaDetectionController.dump(pw);
}
@@ -3535,6 +3556,18 @@
DisplayManagerFlags getFlags() {
return new DisplayManagerFlags();
}
+
+ DisplayPowerController getDisplayPowerController(Context context,
+ DisplayPowerController.Injector injector,
+ DisplayManagerInternal.DisplayPowerCallbacks callbacks, Handler handler,
+ SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
+ BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
+ Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+ boolean bootCompleted, DisplayManagerFlags flags) {
+ return new DisplayPowerController(context, injector, callbacks, handler, sensorManager,
+ blanker, logicalDisplay, brightnessTracker, brightnessSetting,
+ onBrightnessChangeRunnable, hbmMetadata, bootCompleted, flags);
+ }
}
@VisibleForTesting
@@ -3564,7 +3597,7 @@
}
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
- private DisplayPowerControllerInterface addDisplayPowerControllerLocked(
+ private DisplayPowerController addDisplayPowerControllerLocked(
LogicalDisplay display) {
if (mPowerHandler == null) {
// initPowerManagement has not yet been called.
@@ -3578,7 +3611,7 @@
final int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId());
final BrightnessSetting brightnessSetting = new BrightnessSetting(userSerial,
mPersistentDataStore, display, mSyncRoot);
- final DisplayPowerControllerInterface displayPowerController;
+ final DisplayPowerController displayPowerController;
// If display is internal and has a HighBrightnessModeMetadata mapping, use that.
// Or create a new one and use that.
@@ -3587,7 +3620,7 @@
// with the corresponding displaydevice.
HighBrightnessModeMetadata hbmMetadata =
mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
- displayPowerController = new DisplayPowerController(
+ displayPowerController = mInjector.getDisplayPowerController(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
() -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
@@ -3757,7 +3790,7 @@
public boolean mWifiDisplayScanRequested;
- CallbackRecord(int pid, int uid, IDisplayManagerCallback callback,
+ CallbackRecord(int pid, int uid, @NonNull IDisplayManagerCallback callback,
@EventsMask long eventsMask) {
mPid = pid;
mUid = uid;
@@ -3785,7 +3818,9 @@
}
/**
- * @return {@code false} if RemoteException happens; otherwise {@code true} for success.
+ * @return {@code false} if RemoteException happens; otherwise {@code true} for
+ * success. This returns true even if the event was deferred because the remote client is
+ * cached.
*/
public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
if (!shouldSendEvent(event)) {
@@ -3798,9 +3833,19 @@
"notifyDisplayEventAsync#notSendingEvent=" + event + ",mEventsMask="
+ mEventsMask);
}
+ // The client is not interested in this event, so do nothing.
return true;
}
+ return transmitDisplayEvent(displayId, event);
+ }
+
+ /**
+ * Transmit a single display event. The client is presumed ready. Return true on success
+ * and false if the client died.
+ */
+ private boolean transmitDisplayEvent(int displayId, @DisplayEvent int event) {
+ // The client is ready to receive the event.
try {
mCallback.onDisplayEvent(displayId, event);
return true;
@@ -3812,6 +3857,9 @@
}
}
+ /**
+ * Return true if the client is interested in this event.
+ */
private boolean shouldSendEvent(@DisplayEvent int event) {
final long mask = mEventsMask.get();
switch (event) {
@@ -4366,7 +4414,7 @@
uniqueId, userSerial);
if (config == null) {
// Get default configuration
- DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
+ DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
if (dpc != null) {
config = dpc.getDefaultBrightnessConfiguration();
}
@@ -4379,7 +4427,6 @@
}
-
@Override // Binder call
public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
final String uniqueId;
@@ -4420,7 +4467,7 @@
if (display == null || !display.isEnabledLocked()) {
return null;
}
- DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
return dpc.getBrightnessInfo();
}
@@ -4458,14 +4505,16 @@
@Override // Binder call
public void setBrightness(int displayId, float brightness) {
setBrightness_enforcePermission();
- if (!isValidBrightness(brightness)) {
- Slog.w(TAG, "Attempted to set invalid brightness" + brightness);
+ if (Float.isNaN(brightness)) {
+ Slog.w(TAG, "Attempted to set invalid brightness: " + brightness);
return;
}
+ MathUtils.constrain(brightness, PowerManager.BRIGHTNESS_MIN,
+ PowerManager.BRIGHTNESS_MAX);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.setBrightness(brightness);
}
@@ -4485,7 +4534,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
brightness = dpc.getScreenBrightnessSetting();
}
@@ -4757,12 +4806,6 @@
}
}
- private static boolean isValidBrightness(float brightness) {
- return !Float.isNaN(brightness)
- && (brightness >= PowerManager.BRIGHTNESS_MIN)
- && (brightness <= PowerManager.BRIGHTNESS_MAX);
- }
-
@VisibleForTesting
void overrideSensorManager(SensorManager sensorManager) {
synchronized (mSyncRoot) {
@@ -4812,7 +4855,7 @@
id).getPrimaryDisplayDeviceLocked();
final int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
- final DisplayPowerControllerInterface displayPowerController =
+ final DisplayPowerController displayPowerController =
mDisplayPowerControllers.get(id);
if (displayPowerController != null) {
ready &= displayPowerController.requestPowerState(request,
@@ -5193,7 +5236,7 @@
return null;
}
- DisplayPowerControllerInterface displayPowerController =
+ DisplayPowerController displayPowerController =
mDisplayPowerControllers.get(logicalDisplay.getDisplayIdLocked());
if (displayPowerController == null) {
Slog.w(TAG,
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index a188e79..b05a96e 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -39,12 +39,12 @@
@Nullable
private final DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
- private final DisplayPowerControllerInterface mDisplayPowerController;
+ private final DisplayPowerController mDisplayPowerController;
private boolean mIsActive;
public DisplayOffloadSessionImpl(
@Nullable DisplayManagerInternal.DisplayOffloader displayOffloader,
- DisplayPowerControllerInterface displayPowerController) {
+ DisplayPowerController displayPowerController) {
mDisplayOffloader = displayOffloader;
mDisplayPowerController = displayPowerController;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index a887f6d..c3faec0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -126,7 +126,7 @@
* slower by changing the "animator duration scale" option in Development Settings.
*/
final class DisplayPowerController implements AutomaticBrightnessController.Callbacks,
- DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface {
+ DisplayWhiteBalanceController.Callbacks{
private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
@@ -253,10 +253,10 @@
// The display blanker.
private final DisplayBlanker mBlanker;
- // The LogicalDisplay tied to this DisplayPowerController2.
+ // The LogicalDisplay tied to this DisplayPowerController.
private final LogicalDisplay mLogicalDisplay;
- // The ID of the LogicalDisplay tied to this DisplayPowerController2.
+ // The ID of the LogicalDisplay tied to this DisplayPowerController.
private final int mDisplayId;
// The ID of the display which this display follows for brightness purposes.
@@ -466,7 +466,7 @@
private ObjectAnimator mColorFadeOffAnimator;
private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
- // True if this DisplayPowerController2 has been stopped and should no longer be running.
+ // True if this DisplayPowerController has been stopped and should no longer be running.
private boolean mStopped;
private DisplayDeviceConfig mDisplayDeviceConfig;
@@ -481,7 +481,7 @@
// DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
// is one lead display, the additional displays follow the brightness value of the lead display.
@GuardedBy("mLock")
- private final SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
+ private final SparseArray<DisplayPowerController> mDisplayBrightnessFollowers =
new SparseArray();
private boolean mBootCompleted;
@@ -591,7 +591,8 @@
mThermalBrightnessThrottlingDataId,
logicalDisplay.getPowerThrottlingDataIdLocked(),
mDisplayDeviceConfig, displayDeviceInfo.width, displayDeviceInfo.height,
- displayToken, mDisplayId), mContext, flags, mSensorManager);
+ displayToken, mDisplayId), mContext, flags, mSensorManager,
+ mDisplayBrightnessController.getCurrentBrightness());
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
mAutomaticBrightnessStrategy =
@@ -678,7 +679,6 @@
/**
* Returns true if the proximity sensor screen-off function is available.
*/
- @Override
public boolean isProximitySensorAvailable() {
return mDisplayPowerProximityStateController.isProximitySensorAvailable();
}
@@ -690,7 +690,6 @@
* @param includePackage if false will null out the package name in events
*/
@Nullable
- @Override
public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
@UserIdInt int userId, boolean includePackage) {
if (mBrightnessTracker == null) {
@@ -699,7 +698,6 @@
return mBrightnessTracker.getEvents(userId, includePackage);
}
- @Override
public void onSwitchUser(@UserIdInt int newUserId, int userSerial, float newBrightness) {
Message msg = mHandler.obtainMessage(MSG_SWITCH_USER, newUserId, userSerial, newBrightness);
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
@@ -736,7 +734,6 @@
}
@Nullable
- @Override
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@UserIdInt int userId) {
if (mBrightnessTracker == null) {
@@ -748,7 +745,6 @@
/**
* Persist the brightness slider events and ambient brightness stats to disk.
*/
- @Override
public void persistBrightnessTrackerState() {
if (mBrightnessTracker != null) {
mBrightnessTracker.persistBrightnessTrackerState();
@@ -805,7 +801,6 @@
}
}
- @Override
public void overrideDozeScreenState(int displayState, @Display.StateReason int reason) {
Slog.i(TAG, "New offload doze override: " + Display.stateToString(displayState));
if (mDisplayOffloadSession != null
@@ -832,7 +827,6 @@
}
}
- @Override
public void setDisplayOffloadSession(DisplayOffloadSession session) {
if (session == mDisplayOffloadSession) {
return;
@@ -841,7 +835,6 @@
mDisplayOffloadSession = session;
}
- @Override
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
if (mAutomaticBrightnessController == null) {
return null;
@@ -856,12 +849,12 @@
*
* Make sure DisplayManagerService.mSyncRoot lock is held when this is called
*/
- @Override
- public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) {
+ public void onDisplayChanged(@Nullable HighBrightnessModeMetadata hbmMetadata,
+ int leadDisplayId) {
mLeadDisplayId = leadDisplayId;
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
- Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
+ Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
+ mLogicalDisplay.getDisplayIdLocked());
return;
}
@@ -935,10 +928,9 @@
/**
* Unregisters all listeners and interrupts all running threads; halting future work.
*
- * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
+ * This method should be called when the DisplayPowerController is no longer in use; i.e. when
* the {@link #mDisplayId display} has been removed.
*/
- @Override
public void stop() {
synchronized (mLock) {
clearDisplayBrightnessFollowersLocked();
@@ -958,7 +950,7 @@
}
private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
- HighBrightnessModeMetadata hbmMetadata) {
+ @Nullable HighBrightnessModeMetadata hbmMetadata) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness(
@@ -1215,7 +1207,6 @@
}
}
- @Override
public void setAutomaticScreenBrightnessMode(
@AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
Message msg = mHandler.obtainMessage();
@@ -1313,7 +1304,7 @@
boolean mustInitialize = false;
mBrightnessReasonTemp.set(null);
mTempBrightnessEvent.reset();
- SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers;
+ SparseArray<DisplayPowerController> displayBrightnessFollowers;
synchronized (mLock) {
if (mStopped) {
return;
@@ -1546,7 +1537,7 @@
float ambientLux = mAutomaticBrightnessController == null ? 0
: mAutomaticBrightnessController.getAmbientLux();
for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
- DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
+ DisplayPowerController follower = displayBrightnessFollowers.valueAt(i);
follower.setBrightnessToFollow(rawBrightnessState,
mDisplayBrightnessController.convertToNits(rawBrightnessState),
ambientLux, slowChange);
@@ -1588,7 +1579,7 @@
// brightness sources (such as an app override) are not saved to the setting, but should be
// reflected in HBM calculations.
mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState,
- mBrightnessClamperController.getBrightnessMaxReason());
+ clampedState.getBrightnessMaxReason());
// Animate the screen brightness when the screen is on or dozing.
// Skip the animation when the screen is off or suspended.
@@ -1804,7 +1795,7 @@
if (userSetBrightnessChanged
|| newEvent.getReason().getReason() != BrightnessReason.REASON_TEMPORARY) {
- logBrightnessEvent(newEvent, unthrottledBrightnessState);
+ logBrightnessEvent(newEvent, unthrottledBrightnessState, clampedState);
}
if (mBrightnessEventRingBuffer != null) {
mBrightnessEventRingBuffer.append(newEvent);
@@ -1903,7 +1894,6 @@
}
}
- @Override
public void updateBrightness() {
sendUpdatePowerState();
}
@@ -1912,12 +1902,10 @@
* Ignores the proximity sensor until the sensor state changes, but only if the sensor is
* currently enabled and forcing the screen to be dark.
*/
- @Override
public void ignoreProximitySensorUntilChanged() {
mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged();
}
- @Override
public void setBrightnessConfiguration(BrightnessConfiguration c,
boolean shouldResetShortTermModel) {
Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS,
@@ -1925,28 +1913,24 @@
msg.sendToTarget();
}
- @Override
public void setTemporaryBrightness(float brightness) {
Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
Float.floatToIntBits(brightness), 0 /*unused*/);
msg.sendToTarget();
}
- @Override
public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
Float.floatToIntBits(adjustment), 0 /*unused*/);
msg.sendToTarget();
}
- @Override
public void setBrightnessFromOffload(float brightness) {
Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD,
Float.floatToIntBits(brightness), 0 /*unused*/);
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
- @Override
public float[] getAutoBrightnessLevels(
@AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
@@ -1955,7 +1939,6 @@
return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
}
- @Override
public float[] getAutoBrightnessLuxLevels(
@AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
@@ -1964,7 +1947,6 @@
return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
}
- @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
@@ -1978,7 +1960,6 @@
}
}
- @Override
public void onBootCompleted() {
Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
@@ -1997,6 +1978,9 @@
synchronized (mCachedBrightnessInfo) {
float stateMax = state != null ? state.getMaxBrightness() : PowerManager.BRIGHTNESS_MAX;
float stateMin = state != null ? state.getMinBrightness() : PowerManager.BRIGHTNESS_MAX;
+ @BrightnessInfo.BrightnessMaxReason int maxReason =
+ state != null ? state.getBrightnessMaxReason()
+ : BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
final float minBrightness = Math.max(stateMin, Math.min(
mBrightnessRangeController.getCurrentBrightnessMin(), stateMax));
final float maxBrightness = Math.min(
@@ -2023,7 +2007,7 @@
mBrightnessRangeController.getTransitionPoint());
changed |=
mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
- mBrightnessClamperController.getBrightnessMaxReason());
+ maxReason);
return changed;
}
}
@@ -2239,6 +2223,7 @@
unblockScreenOn();
}
mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
+ Slog.i(TAG, "Window Manager Policy screenTurningOn complete");
}
// Return true if the screen isn't blocked.
@@ -2491,18 +2476,14 @@
}
}
-
- @Override
public float getScreenBrightnessSetting() {
return mDisplayBrightnessController.getScreenBrightnessSetting();
}
- @Override
public float getDozeBrightnessForOffload() {
return mDisplayBrightnessController.getCurrentBrightness() * mDozeScaleFactor;
}
- @Override
public void setBrightness(float brightness) {
// After HBMController and NBMController migration to Clampers framework
// currentBrightnessMax should be taken from clampers controller
@@ -2511,7 +2492,6 @@
mBrightnessRangeController.getCurrentBrightnessMax());
}
- @Override
public void setBrightness(float brightness, int userSerial) {
// After HBMController and NBMController migration to Clampers framework
// currentBrightnessMax should be taken from clampers controller
@@ -2520,17 +2500,14 @@
mBrightnessRangeController.getCurrentBrightnessMax());
}
- @Override
public int getDisplayId() {
return mDisplayId;
}
- @Override
public int getLeadDisplayId() {
return mLeadDisplayId;
}
- @Override
public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
boolean slowChange) {
mBrightnessRangeController.onAmbientLuxChange(ambientLux);
@@ -2591,16 +2568,14 @@
mAutomaticBrightnessController.getLastSensorTimestamps());
}
- @Override
- public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
+ public void addDisplayBrightnessFollower(DisplayPowerController follower) {
synchronized (mLock) {
mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower);
sendUpdatePowerStateLocked();
}
}
- @Override
- public void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
+ public void removeDisplayBrightnessFollower(DisplayPowerController follower) {
synchronized (mLock) {
mDisplayBrightnessFollowers.remove(follower.getDisplayId());
mHandler.postAtTime(() -> follower.setBrightnessToFollow(
@@ -2612,7 +2587,7 @@
@GuardedBy("mLock")
private void clearDisplayBrightnessFollowersLocked() {
for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
- DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
+ DisplayPowerController follower = mDisplayBrightnessFollowers.valueAt(i);
mHandler.postAtTime(() -> follower.setBrightnessToFollow(
PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS,
/* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis());
@@ -2620,11 +2595,12 @@
mDisplayBrightnessFollowers.clear();
}
- @Override
public void dump(final PrintWriter pw) {
synchronized (mLock) {
pw.println();
pw.println("Display Power Controller:");
+ pw.println("-------------------------");
+
pw.println(" mDisplayId=" + mDisplayId);
pw.println(" mLeadDisplayId=" + mLeadDisplayId);
pw.println(" mLightSensor=" + mLightSensor);
@@ -2699,25 +2675,39 @@
+ mColorFadeOffAnimator.isStarted());
}
+ pw.println();
if (mPowerState != null) {
mPowerState.dump(pw);
}
- if (mAutomaticBrightnessController != null) {
- mAutomaticBrightnessController.dump(pw);
- dumpBrightnessEvents(pw);
+ pw.println();
+ if (mDisplayBrightnessController != null) {
+ mDisplayBrightnessController.dump(pw);
}
- dumpRbcEvents(pw);
-
- if (mScreenOffBrightnessSensorController != null) {
- mScreenOffBrightnessSensorController.dump(pw);
+ pw.println();
+ if (mBrightnessClamperController != null) {
+ mBrightnessClamperController.dump(ipw);
}
+ pw.println();
if (mBrightnessRangeController != null) {
mBrightnessRangeController.dump(pw);
}
+ pw.println();
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.dump(pw);
+ dumpBrightnessEvents(pw);
+ }
+ dumpRbcEvents(pw);
+
+ pw.println();
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.dump(pw);
+ }
+
+ pw.println();
if (mBrightnessThrottler != null) {
mBrightnessThrottler.dump(pw);
}
@@ -2729,24 +2719,13 @@
}
pw.println();
-
if (mWakelockController != null) {
mWakelockController.dumpLocal(pw);
}
pw.println();
- if (mDisplayBrightnessController != null) {
- mDisplayBrightnessController.dump(pw);
- }
-
- pw.println();
if (mDisplayStateController != null) {
- mDisplayStateController.dumpsys(pw);
- }
-
- pw.println();
- if (mBrightnessClamperController != null) {
- mBrightnessClamperController.dump(ipw);
+ mDisplayStateController.dump(pw);
}
}
@@ -2926,7 +2905,8 @@
return FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_UNKNOWN;
}
- private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness) {
+ private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness,
+ DisplayBrightnessState brightnessState) {
int modifier = event.getReason().getModifier();
int flags = event.getFlags();
// It's easier to check if the brightness is at maximum level using the brightness
@@ -2963,7 +2943,7 @@
event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
(modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0,
- mBrightnessClamperController.getBrightnessMaxReason(),
+ brightnessState.getBrightnessMaxReason(),
// TODO: (flc) add brightnessMinReason here too.
(modifier & BrightnessReason.MODIFIER_DIMMED) > 0,
event.isRbcEnabled(),
@@ -3151,19 +3131,17 @@
}
}
- @Override
public void setAutoBrightnessLoggingEnabled(boolean enabled) {
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.setLoggingEnabled(enabled);
}
}
- @Override // DisplayWhiteBalanceController.Callbacks
+ // DisplayWhiteBalanceController.Callbacks
public void updateWhiteBalance() {
sendUpdatePowerState();
}
- @Override
public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
Message msg = mHandler.obtainMessage();
msg.what = MSG_SET_DWBC_LOGGING_ENABLED;
@@ -3171,7 +3149,6 @@
msg.sendToTarget();
}
- @Override
public void setAmbientColorTemperatureOverride(float cct) {
Message msg = mHandler.obtainMessage();
msg.what = MSG_SET_DWBC_COLOR_OVERRIDE;
@@ -3296,10 +3273,10 @@
BrightnessClamperController getBrightnessClamperController(Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data, Context context,
- DisplayManagerFlags flags, SensorManager sensorManager) {
+ DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
return new BrightnessClamperController(handler, clamperChangeListener, data, context,
- flags, sensorManager);
+ flags, sensorManager, currentBrightness);
}
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
deleted file mode 100644
index d28578a..0000000
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import android.content.pm.ParceledListSlice;
-import android.hardware.display.AmbientBrightnessDayStats;
-import android.hardware.display.BrightnessChangeEvent;
-import android.hardware.display.BrightnessConfiguration;
-import android.hardware.display.BrightnessInfo;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.PowerManager;
-import android.view.Display;
-
-import java.io.PrintWriter;
-
-/**
- * An interface to manage the display's power state and brightness
- */
-public interface DisplayPowerControllerInterface {
- /**
- * Notified when the display is changed.
- *
- * We use this to apply any changes that might be needed when displays get swapped on foldable
- * devices, when layouts change, etc.
- *
- * Must be called while holding the SyncRoot lock.
- *
- * @param hbmInfo The high brightness mode metadata, like
- * remaining time and hbm events, for the corresponding
- * physical display, to make sure we stay within the safety margins.
- * @param leadDisplayId The display who is considered our "leader" for things like brightness.
- */
- void onDisplayChanged(HighBrightnessModeMetadata hbmInfo, int leadDisplayId);
-
- /**
- * Unregisters all listeners and interrupts all running threads; halting future work.
- *
- * This method should be called when the DisplayPowerController is no longer in use; i.e. when
- * the display has been removed.
- */
- void stop();
-
- /**
- * Used to update the display's BrightnessConfiguration
- * @param config The new BrightnessConfiguration
- */
- void setBrightnessConfiguration(BrightnessConfiguration config,
- boolean shouldResetShortTermModel);
-
- /**
- * Used to set the ambient color temperature of the Display
- * @param ambientColorTemperature The target ambientColorTemperature
- */
- void setAmbientColorTemperatureOverride(float ambientColorTemperature);
-
- /**
- * Used to decide the associated AutomaticBrightnessController's BrightnessMode
- * @param mode The auto-brightness mode
- */
- void setAutomaticScreenBrightnessMode(
- @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
-
- /**
- * Used to enable/disable the logging of the WhileBalance associated entities
- * @param enabled Flag which represents if the logging is the be enabled
- */
- void setDisplayWhiteBalanceLoggingEnabled(boolean enabled);
-
- /**
- * Used to dump the state.
- * @param writer The PrintWriter used to dump the state.
- */
- void dump(PrintWriter writer);
-
- /**
- * Used to get the ambient brightness stats
- */
- ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId);
-
- /**
- * Get the default brightness configuration
- */
- BrightnessConfiguration getDefaultBrightnessConfiguration();
-
- /**
- * Set the screen brightness of the associated display
- * @param brightness The value to which the brightness is to be set
- */
- void setBrightness(float brightness);
-
- /**
- * Set the screen brightness of the associated display
- * @param brightness The value to which the brightness is to be set
- * @param userSerial The user for which the brightness value is to be set.
- */
- void setBrightness(float brightness, int userSerial);
-
- /**
- * Checks if the proximity sensor is available
- */
- boolean isProximitySensorAvailable();
-
- /**
- * Persist the brightness slider events and ambient brightness stats to disk.
- */
- void persistBrightnessTrackerState();
-
- /**
- * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
- * currently enabled and forcing the screen to be dark.
- */
- void ignoreProximitySensorUntilChanged();
-
- /**
- * Requests a new power state.
- *
- * @param request The requested power state.
- * @param waitForNegativeProximity If true, issues a request to wait for
- * negative proximity before turning the screen back on,
- * assuming the screen was turned off by the proximity sensor.
- * @return True if display is ready, false if there are important changes that must
- * be made asynchronously.
- */
- boolean requestPowerState(DisplayManagerInternal.DisplayPowerRequest request,
- boolean waitForNegativeProximity);
-
- /**
- * Overrides the current doze screen state.
- *
- * @param displayState the new doze display state.
- * @param reason the reason behind the new doze display state.
- */
- void overrideDozeScreenState(int displayState, @Display.StateReason int reason);
-
- void setDisplayOffloadSession(DisplayManagerInternal.DisplayOffloadSession session);
-
- /**
- * Sets up the temporary autobrightness adjustment when the user is yet to settle down to a
- * value.
- */
- void setTemporaryAutoBrightnessAdjustment(float adjustment);
-
- /**
- * Sets temporary brightness from the offload chip until we get a brightness value from
- * the light sensor.
- * @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and
- * {@link PowerManager.BRIGHTNESS_MAX}. Values outside of that range will be ignored.
- */
- void setBrightnessFromOffload(float brightness);
-
- /**
- * Gets the screen brightness setting
- */
- float getScreenBrightnessSetting();
-
- /**
- * Gets the brightness value used when the device is in doze
- */
- float getDozeBrightnessForOffload();
-
- /**
- * Sets up the temporary brightness for the associated display
- */
- void setTemporaryBrightness(float brightness);
-
- /**
- * Gets the associated {@link BrightnessInfo}
- */
- BrightnessInfo getBrightnessInfo();
-
- /**
- * Get the {@link BrightnessChangeEvent}s for the specified user.
- */
- ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(int userId, boolean hasUsageStats);
-
- /**
- * Sets up the logging for the associated {@link AutomaticBrightnessController}
- * @param enabled Flag to represent if the logging is to be enabled
- */
- void setAutoBrightnessLoggingEnabled(boolean enabled);
-
- /**
- * Handles the changes to be done to update the brightness when the user is changed
- * @param newUserId The new userId
- * @param userSerial The serial number of the new user
- * @param newBrightness The brightness for the new user
- */
- void onSwitchUser(int newUserId, int userSerial, float newBrightness);
-
- /**
- * Get the ID of the display associated with this DPC.
- * @return The display ID
- */
- int getDisplayId();
-
- /**
- * Get the ID of the display that is the leader of this DPC.
- *
- * Note that this is different than the display associated with the DPC. The leader is another
- * display which we follow for things like brightness.
- *
- * Must be called while holding the SyncRoot lock.
- */
- int getLeadDisplayId();
-
- /**
- * Set the brightness to follow if this is an additional display in a set of concurrent
- * displays.
- * @param leadDisplayBrightness The brightness of the lead display in the set of concurrent
- * displays
- * @param nits The brightness value in nits if the device supports nits. Set to a negative
- * number otherwise.
- * @param ambientLux The lux value that will be passed to {@link HighBrightnessModeController}
- * @param slowChange Indicates whether we should slowly animate to the given brightness value.
- */
- void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
- boolean slowChange);
-
- /**
- * Add an additional display that will copy the brightness value from this display. This is used
- * when the device is in concurrent displays mode.
- * @param follower The DPC that should copy the brightness value from this DPC
- */
- void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower);
-
- /**
- * Removes the given display from the list of brightness followers.
- * @param follower The DPC to remove from the followers list
- */
- void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower);
-
- /**
- * Indicate that boot has been completed and the screen is ready to update.
- */
- void onBootCompleted();
-
- /**
- * Get the brightness levels used to determine automatic brightness based on lux levels.
- * @param mode The auto-brightness mode
- * @return The brightness levels for the specified mode. The values are between
- * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
- */
- float[] getAutoBrightnessLevels(
- @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
-
- /**
- * Get the lux levels used to determine automatic brightness.
- * @param mode The auto-brightness mode
- * @return The lux levels for the specified mode
- */
- float[] getAutoBrightnessLuxLevels(
- @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
-}
diff --git a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
index 882c02f..215932c 100644
--- a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
@@ -315,6 +315,7 @@
public void dumpLocal(PrintWriter pw) {
pw.println();
pw.println("DisplayPowerProximityStateController:");
+ pw.println("-------------------------------------");
synchronized (mLock) {
pw.println(" mPendingWaitForNegativeProximityLocked="
+ mPendingWaitForNegativeProximityLocked);
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index e5efebc..2fbb114 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -344,7 +344,6 @@
}
public void dump(PrintWriter pw) {
- pw.println();
pw.println("Display Power State:");
pw.println(" mStopped=" + mStopped);
pw.println(" mScreenState=" + Display.stateToString(mScreenState));
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index da9eef2..135cab6 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -266,7 +266,7 @@
mSettingsObserver.stopObserving();
}
- void setHighBrightnessModeMetadata(HighBrightnessModeMetadata hbmInfo) {
+ void setHighBrightnessModeMetadata(@Nullable HighBrightnessModeMetadata hbmInfo) {
mHighBrightnessModeMetadata = hbmInfo;
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 6e0b9cf..0570b2a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -1286,7 +1286,10 @@
pw.println(" " + mSupportedModes.valueAt(i));
}
pw.println("mSupportedColorModes=" + mSupportedColorModes);
- pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
+ pw.println("");
+ pw.println("DisplayDeviceConfig: ");
+ pw.println("---------------------");
+ pw.println(mDisplayDeviceConfig);
}
private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5d55d190..e8be8a4 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -1040,6 +1040,9 @@
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
+ pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null
+ ? mPrimaryDisplayDevice.getNameLocked() + "(" + mPrimaryDisplayDevice.getUniqueId()
+ + ")" : "null"));
pw.println("mIsEnabled=" + mIsEnabled);
pw.println("mIsInTransition=" + mIsInTransition);
pw.println("mLayerStack=" + mLayerStack);
@@ -1049,8 +1052,6 @@
pw.println("mRequestedColorMode=" + mRequestedColorMode);
pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
pw.println("mDisplayScalingDisabled=" + mDisplayScalingDisabled);
- pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
- mPrimaryDisplayDevice.getNameLocked() : "null"));
pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index e9ecfc6..c3f6a52 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -427,6 +427,7 @@
public void dumpLocked(PrintWriter pw) {
pw.println("LogicalDisplayMapper:");
+ pw.println("---------------------");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.increaseIndent();
@@ -477,14 +478,14 @@
// The boot animation might still be in progress, we do not want to switch states now
// as the boot animation would end up with an incorrect size.
if (DEBUG) {
- Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState
- + " until boot is completed");
+ Slog.d(TAG, "Postponing transition to state: "
+ + mPendingDeviceState.getIdentifier() + " until boot is completed");
}
mDeviceStateToBeAppliedAfterBoot = state;
return;
}
- Slog.i(TAG, "Requesting Transition to state: " + state + ", from state="
+ Slog.i(TAG, "Requesting Transition to state: " + state.getIdentifier() + ", from state="
+ mDeviceState.getIdentifier() + ", interactive=" + mInteractive
+ ", mBootCompleted=" + mBootCompleted);
// As part of a state transition, we may need to turn off some displays temporarily so that
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 2d7792d..9cdc918 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -628,7 +628,9 @@
}
public void dump(PrintWriter pw) {
- pw.println("PersistentDataStore");
+ pw.println("PersistentDataStore:");
+ pw.println("--------------------");
+
pw.println(" mLoaded=" + mLoaded);
pw.println(" mDirty=" + mDirty);
pw.println(" RememberedWifiDisplays:");
diff --git a/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
index 0a884c9..b63eba3 100644
--- a/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
+++ b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
@@ -121,6 +121,7 @@
/** Dump current state */
public void dump(PrintWriter pw) {
pw.println("Screen Off Brightness Sensor Controller:");
+ pw.println("----------------------------------------");
IndentingPrintWriter idpw = new IndentingPrintWriter(pw);
idpw.increaseIndent();
idpw.println("registered=" + mRegistered);
diff --git a/services/core/java/com/android/server/display/SmallAreaDetectionController.java b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
index bf384b0..3ed7e57 100644
--- a/services/core/java/com/android/server/display/SmallAreaDetectionController.java
+++ b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
@@ -133,7 +133,8 @@
}
void dump(PrintWriter pw) {
- pw.println("Small area detection allowlist");
+ pw.println("Small area detection allowlist:");
+ pw.println("-------------------------------");
pw.println(" Packages:");
synchronized (mLock) {
for (String pkg : mAllowPkgMap.keySet()) {
diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java
index 5b0229c..3520723 100644
--- a/services/core/java/com/android/server/display/WakelockController.java
+++ b/services/core/java/com/android/server/display/WakelockController.java
@@ -417,6 +417,7 @@
*/
public void dumpLocal(PrintWriter pw) {
pw.println("WakelockController State:");
+ pw.println("-------------------------");
pw.println(" mDisplayId=" + mDisplayId);
pw.println(" mUnfinishedBusiness=" + hasUnfinishedBusiness());
pw.println(" mOnStateChangePending=" + isOnStateChangedPending());
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 1b49bbc..06890e7 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -262,6 +262,7 @@
public void dump(PrintWriter writer) {
writer.println();
writer.println("DisplayBrightnessStrategySelector:");
+ writer.println("----------------------------------");
writer.println(" mDisplayId= " + mDisplayId);
writer.println(" mOldBrightnessStrategyName= " + mOldBrightnessStrategyName);
writer.println(
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index 40e9198..cf44ac0 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -73,7 +73,6 @@
abstract void stop();
protected enum Type {
- THERMAL,
POWER,
WEAR_BEDTIME_MODE,
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 59fffe7..9404034 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -72,13 +72,18 @@
private final List<DisplayDeviceDataListener> mDisplayDeviceDataListeners = new ArrayList<>();
private final List<StatefulModifier> mStatefulModifiers = new ArrayList<>();
private final List<UserSwitchListener> mUserSwitchListeners = new ArrayList<>();
+ private final List<DeviceConfigListener> mDeviceConfigListeners = new ArrayList<>();
+
private ModifiersAggregatedState mModifiersAggregatedState = new ModifiersAggregatedState();
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
+ private final DisplayManagerFlags mDisplayManagerFlags;
private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
@Nullable
+ private BrightnessPowerClamper mPowerClamper;
+ @Nullable
private Type mClamperType = null;
private boolean mClamperApplied = false;
@@ -93,16 +98,18 @@
public BrightnessClamperController(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
- DisplayManagerFlags flags, SensorManager sensorManager) {
- this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager);
+ DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
+ this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager,
+ currentBrightness);
}
@VisibleForTesting
BrightnessClamperController(Injector injector, Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
- DisplayManagerFlags flags, SensorManager sensorManager) {
+ DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mHandler = handler;
+ mDisplayManagerFlags = flags;
mLightSensorController = injector.getLightSensorController(sensorManager, context,
mLightSensorListener, mHandler);
@@ -117,7 +124,15 @@
};
mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
- context);
+ context, currentBrightness);
+ if (mDisplayManagerFlags.isPowerThrottlingClamperEnabled()) {
+ for (BrightnessClamper clamper: mClampers) {
+ if (clamper.getType() == Type.POWER) {
+ mPowerClamper = (BrightnessPowerClamper) clamper;
+ break;
+ }
+ }
+ }
mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener,
data);
@@ -131,9 +146,14 @@
if (m instanceof UserSwitchListener l) {
mUserSwitchListeners.add(l);
}
+ if (m instanceof DeviceConfigListener l) {
+ mDeviceConfigListeners.add(l);
+ }
});
- mOnPropertiesChangedListener =
- properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
+ mOnPropertiesChangedListener = properties -> {
+ mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
+ mDeviceConfigListeners.forEach(DeviceConfigListener::onDeviceConfigChanged);
+ };
mLightSensorController.configure(data.getAmbientLightSensor(), data.getDisplayId());
start();
}
@@ -161,6 +181,7 @@
builder.setBrightness(cappedBrightness);
builder.setMaxBrightness(mBrightnessCap);
builder.setCustomAnimationRate(mCustomAnimationRate);
+ builder.setBrightnessMaxReason(getBrightnessMaxReason());
if (mClamperType != null) {
builder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
@@ -182,26 +203,19 @@
mModifiers.get(i).apply(request, builder);
}
+ if (mDisplayManagerFlags.isPowerThrottlingClamperEnabled()) {
+ if (mPowerClamper != null) {
+ mPowerClamper.updateCurrentBrightness(cappedBrightness);
+ }
+ }
+
return builder.build();
}
- /**
- * See BrightnessThrottler.getBrightnessMaxReason:
- * used in:
- * 1) DPC2.CachedBrightnessInfo to determine changes
- * 2) DPC2.logBrightnessEvent
- * 3) HBMController - for logging
- * Method is called in mHandler thread (DisplayControllerHandler), in the same thread
- * recalculateBrightnessCap and DPC2.updatePowerStateInternal are called.
- * Should be moved to DisplayBrightnessState OR derived from DisplayBrightnessState
- * TODO: b/263362199
- */
@BrightnessInfo.BrightnessMaxReason
- public int getBrightnessMaxReason() {
+ private int getBrightnessMaxReason() {
if (mClamperType == null) {
return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
- } else if (mClamperType == Type.THERMAL) {
- return BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
} else if (mClamperType == Type.POWER) {
return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC;
} else if (mClamperType == Type.WEAR_BEDTIME_MODE) {
@@ -216,7 +230,7 @@
* Called when the user switches.
*/
public void onUserSwitch() {
- mUserSwitchListeners.forEach(listener -> listener.onSwitchUser());
+ mUserSwitchListeners.forEach(UserSwitchListener::onSwitchUser);
}
/**
@@ -224,6 +238,7 @@
*/
public void dump(PrintWriter writer) {
writer.println("BrightnessClamperController:");
+ writer.println("----------------------------");
writer.println(" mBrightnessCap: " + mBrightnessCap);
writer.println(" mClamperType: " + mClamperType);
writer.println(" mClamperApplied: " + mClamperApplied);
@@ -284,11 +299,14 @@
state2.mMaxDesiredHdrRatio)
|| !BrightnessSynchronizer.floatEquals(state1.mMaxHdrBrightness,
state2.mMaxHdrBrightness)
- || state1.mSdrHdrRatioSpline != state2.mSdrHdrRatioSpline;
+ || state1.mSdrHdrRatioSpline != state2.mSdrHdrRatioSpline
+ || state1.mMaxBrightnessReason != state2.mMaxBrightnessReason
+ || !BrightnessSynchronizer.floatEquals(state1.mMaxBrightness,
+ state2.mMaxBrightness);
}
private void start() {
- if (!mClampers.isEmpty()) {
+ if (!mClampers.isEmpty() || !mDeviceConfigListeners.isEmpty()) {
mDeviceConfigParameterProvider.addOnPropertiesChangedListener(
mExecutor, mOnPropertiesChangedListener);
}
@@ -321,13 +339,16 @@
List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
- DisplayManagerFlags flags, Context context) {
+ DisplayManagerFlags flags, Context context, float currentBrightness) {
List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
- clampers.add(
- new BrightnessThermalClamper(handler, clamperChangeListener, data));
+
if (flags.isPowerThrottlingClamperEnabled()) {
- clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
- data));
+ // Check if power-throttling config is present.
+ PowerThrottlingConfigData configData = data.getPowerThrottlingConfigData();
+ if (configData != null) {
+ clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
+ data, currentBrightness));
+ }
}
if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
@@ -340,6 +361,8 @@
Handler handler, ClamperChangeListener listener,
DisplayDeviceData data) {
List<BrightnessStateModifier> modifiers = new ArrayList<>();
+ modifiers.add(new BrightnessThermalModifier(handler, listener, data));
+
modifiers.add(new DisplayDimModifier(context));
modifiers.add(new BrightnessLowPowerModeModifier());
if (flags.isEvenDimmerEnabled() && data.mDisplayDeviceConfig.isEvenDimmerAvailable()) {
@@ -370,7 +393,7 @@
/**
* Config Data for clampers/modifiers
*/
- public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
+ public static class DisplayDeviceData implements BrightnessThermalModifier.ThermalData,
BrightnessPowerClamper.PowerData,
BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
@NonNull
@@ -484,13 +507,23 @@
}
/**
+ * Modifier should implement this interface in order to receive device config updates
+ */
+ interface DeviceConfigListener {
+ void onDeviceConfigChanged();
+ }
+
+ /**
* StatefulModifiers contribute to AggregatedState, that is used to decide if brightness
- * adjustement is needed
+ * adjustment is needed
*/
public static class ModifiersAggregatedState {
float mMaxDesiredHdrRatio = HdrBrightnessModifier.DEFAULT_MAX_HDR_SDR_RATIO;
float mMaxHdrBrightness = PowerManager.BRIGHTNESS_MAX;
@Nullable
Spline mSdrHdrRatioSpline = null;
+ @BrightnessInfo.BrightnessMaxReason
+ int mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+ float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
index 790322d..85e81f9 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
@@ -21,16 +21,23 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.Temperature;
import android.provider.DeviceConfigInterface;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel;
+import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DeviceConfigParsingUtils;
@@ -65,14 +72,21 @@
private PowerThrottlingData mPowerThrottlingDataActive = null;
@Nullable
private PowerThrottlingConfigData mPowerThrottlingConfigData = null;
-
+ @NonNull
+ private final ThermalLevelListener mThermalLevelListener;
+ @NonNull
+ private final PowerChangeListener mPowerChangeListener;
private @Temperature.ThrottlingStatus int mCurrentThermalLevel = Temperature.THROTTLING_NONE;
+ private boolean mCurrentThermalLevelChanged = false;
private float mCurrentAvgPowerConsumed = 0;
@Nullable
private String mUniqueDisplayId = null;
@Nullable
private String mDataId = null;
-
+ private float mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID;
+ private float mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
+ private float mCustomAnimationRateSecDeviceConfig =
+ DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
try {
int status = DeviceConfigParsingUtils.parseThermalStatus(key);
@@ -88,23 +102,41 @@
BrightnessPowerClamper(Handler handler, ClamperChangeListener listener,
- PowerData powerData) {
- this(new Injector(), handler, listener, powerData);
+ PowerData powerData, float currentBrightness) {
+ this(new Injector(), handler, listener, powerData, currentBrightness);
}
@VisibleForTesting
BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
- PowerData powerData) {
+ PowerData powerData, float currentBrightness) {
super(handler, listener);
mInjector = injector;
- mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+ mCurrentBrightness = currentBrightness;
+ mPowerChangeListener = (powerConsumed, thermalStatus) -> {
+ recalculatePowerQuotaChange(powerConsumed, thermalStatus);
+ };
+ mPowerThrottlingConfigData = powerData.getPowerThrottlingConfigData();
+ if (mPowerThrottlingConfigData != null) {
+ mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
+ }
+ mThermalLevelListener = new ThermalLevelListener(handler);
+ mPmicMonitor =
+ mInjector.getPmicMonitor(mPowerChangeListener,
+ mThermalLevelListener.getThermalService(),
+ mPowerThrottlingConfigData.pollingWindowMaxMillis,
+ mPowerThrottlingConfigData.pollingWindowMinMillis);
+ mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mHandler.post(() -> {
setDisplayData(powerData);
loadOverrideData();
start();
});
+ }
+ @VisibleForTesting
+ PowerChangeListener getPowerChangeListener() {
+ return mPowerChangeListener;
}
@Override
@@ -114,6 +146,11 @@
}
@Override
+ float getCustomAnimationRate() {
+ return mCustomAnimationRateSec;
+ }
+
+ @Override
void onDeviceConfigChanged() {
mHandler.post(() -> {
loadOverrideData();
@@ -134,6 +171,9 @@
if (mPmicMonitor != null) {
mPmicMonitor.shutdown();
}
+ if (mThermalLevelListener != null) {
+ mThermalLevelListener.stop();
+ }
}
/**
@@ -144,11 +184,20 @@
pw.println(" mCurrentAvgPowerConsumed=" + mCurrentAvgPowerConsumed);
pw.println(" mUniqueDisplayId=" + mUniqueDisplayId);
pw.println(" mCurrentThermalLevel=" + mCurrentThermalLevel);
+ pw.println(" mCurrentThermalLevelChanged=" + mCurrentThermalLevelChanged);
pw.println(" mPowerThrottlingDataFromDDC=" + (mPowerThrottlingDataFromDDC == null ? "null"
: mPowerThrottlingDataFromDDC.toString()));
+ mThermalLevelListener.dump(pw);
super.dump(pw);
}
+ /**
+ * Updates current brightness, for power calculations.
+ */
+ public void updateCurrentBrightness(float currentBrightness) {
+ mCurrentBrightness = currentBrightness;
+ }
+
private void recalculateActiveData() {
if (mUniqueDisplayId == null || mDataId == null) {
return;
@@ -156,17 +205,11 @@
mPowerThrottlingDataActive = mPowerThrottlingDataOverride
.getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
mPowerThrottlingDataFromDDC);
- if (mPowerThrottlingDataActive != null) {
- if (mPmicMonitor != null) {
- mPmicMonitor.stop();
- mPmicMonitor.start();
- }
- } else {
+ if (mPowerThrottlingDataActive == null) {
if (mPmicMonitor != null) {
mPmicMonitor.stop();
}
}
- recalculateBrightnessCap();
}
private void loadOverrideData() {
@@ -198,21 +241,57 @@
if (mPowerThrottlingDataActive == null) {
return;
}
- if (powerQuota > 0 && mCurrentAvgPowerConsumed > powerQuota) {
- isActive = true;
- // calculate new brightness Cap.
- // Brightness has a linear relation to power-consumed.
- targetBrightnessCap =
- (powerQuota / mCurrentAvgPowerConsumed) * PowerManager.BRIGHTNESS_MAX;
- // Cap to lowest allowed brightness on device.
+ if (powerQuota > 0) {
+ if (BrightnessUtils.isValidBrightnessValue(mCurrentBrightness)
+ && (mCurrentAvgPowerConsumed > powerQuota)) {
+ isActive = true;
+ // calculate new brightness Cap.
+ // Brightness has a linear relation to power-consumed.
+ targetBrightnessCap =
+ (powerQuota / mCurrentAvgPowerConsumed) * mCurrentBrightness;
+ } else if (mCurrentThermalLevelChanged) {
+ if (mCurrentThermalLevel == Temperature.THROTTLING_NONE) {
+ // reset pmic and remove the power-throttling cap.
+ isActive = true;
+ targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ mPmicMonitor.stop();
+ } else {
+ isActive = true;
+ // Since the thermal status has changed, we need to remove power-throttling cap.
+ // Instead of recalculating and changing brightness again, adding flicker,
+ // we will wait for the next pmic cycle to re-evaluate this value
+ // make act on it, if needed.
+ targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ if (mPmicMonitor.isStopped()) {
+ mPmicMonitor.start();
+ }
+ }
+ } else { // Current power consumed is under the quota.
+ isActive = true;
+ targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ }
+ }
+
+ // Cap to lowest allowed brightness on device.
+ if (mPowerThrottlingConfigData != null) {
targetBrightnessCap = Math.max(targetBrightnessCap,
- mPowerThrottlingConfigData.brightnessLowestCapAllowed);
+ mPowerThrottlingConfigData.brightnessLowestCapAllowed);
}
if (mBrightnessCap != targetBrightnessCap || mIsActive != isActive) {
mIsActive = isActive;
+ Slog.i(TAG, "Power clamper changing current brightness cap mBrightnessCap: "
+ + mBrightnessCap + " to target brightness cap:" + targetBrightnessCap
+ + " for current screen brightness: " + mCurrentBrightness);
mBrightnessCap = targetBrightnessCap;
+ Slog.i(TAG, "Power clamper changed state: thermalStatus:" + mCurrentThermalLevel
+ + " mCurrentThermalLevelChanged:" + mCurrentThermalLevelChanged
+ + " mCurrentAvgPowerConsumed:" + mCurrentAvgPowerConsumed
+ + " mCustomAnimationRateSec:" + mCustomAnimationRateSecDeviceConfig);
+ mCustomAnimationRateSec = mCustomAnimationRateSecDeviceConfig;
mChangeListener.onChanged();
+ } else {
+ mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
}
}
@@ -234,6 +313,11 @@
private void recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus) {
mHandler.post(() -> {
+ if (mCurrentThermalLevel != thermalStatus) {
+ mCurrentThermalLevelChanged = true;
+ } else {
+ mCurrentThermalLevelChanged = false;
+ }
mCurrentThermalLevel = thermalStatus;
mCurrentAvgPowerConsumed = avgPowerConsumed;
recalculateBrightnessCap();
@@ -244,14 +328,107 @@
if (mPowerThrottlingConfigData == null) {
return;
}
- PowerChangeListener listener = (powerConsumed, thermalStatus) -> {
- recalculatePowerQuotaChange(powerConsumed, thermalStatus);
- };
- mPmicMonitor =
- mInjector.getPmicMonitor(listener, mPowerThrottlingConfigData.pollingWindowMillis);
+ if (mPowerThrottlingConfigData.pollingWindowMaxMillis
+ <= mPowerThrottlingConfigData.pollingWindowMinMillis) {
+ Slog.e(TAG, "Brightness power max polling window:"
+ + mPowerThrottlingConfigData.pollingWindowMaxMillis
+ + " msec, should be greater than brightness min polling window:"
+ + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
+ return;
+ }
+ if ((mPowerThrottlingConfigData.pollingWindowMaxMillis
+ % mPowerThrottlingConfigData.pollingWindowMinMillis) != 0) {
+ Slog.e(TAG, "Brightness power max polling window:"
+ + mPowerThrottlingConfigData.pollingWindowMaxMillis
+ + " msec, is not divisible by brightness min polling window:"
+ + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
+ return;
+ }
+ mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
+ mThermalLevelListener.start();
+ }
+
+ private void activatePmicMonitor() {
+ if (!mPmicMonitor.isStopped()) {
+ return;
+ }
mPmicMonitor.start();
}
+ private void deactivatePmicMonitor(@Temperature.ThrottlingStatus int status) {
+ if (status != Temperature.THROTTLING_NONE) {
+ return;
+ }
+ if (mPmicMonitor.isStopped()) {
+ return;
+ }
+ mPmicMonitor.stop();
+ }
+
+ private final class ThermalLevelListener extends IThermalEventListener.Stub {
+ private final Handler mHandler;
+ private IThermalService mThermalService;
+ private boolean mStarted;
+
+ ThermalLevelListener(Handler handler) {
+ mHandler = handler;
+ mStarted = false;
+ mThermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ }
+
+ IThermalService getThermalService() {
+ return mThermalService;
+ }
+
+ void start() {
+ if (mStarted) {
+ return;
+ }
+ if (mThermalService == null) {
+ return;
+ }
+ try {
+ // TODO b/279114539 Try DISPLAY first and then fallback to SKIN.
+ mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+ mStarted = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ }
+ }
+
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ @Temperature.ThrottlingStatus int status = temp.getStatus();
+ if (status >= Temperature.THROTTLING_LIGHT) {
+ Slog.d(TAG, "Activating pmic monitor due to thermal state:" + status);
+ mHandler.post(() -> activatePmicMonitor());
+ } else {
+ if (!mPmicMonitor.isStopped()) {
+ mHandler.post(() -> deactivatePmicMonitor(status));
+ }
+ }
+ }
+
+ void stop() {
+ if (!mStarted) {
+ return;
+ }
+ try {
+ mThermalService.unregisterThermalEventListener(this);
+ mStarted = false;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ mThermalService = null;
+ }
+
+ void dump(PrintWriter writer) {
+ writer.println(" ThermalLevelObserver:");
+ writer.println(" mStarted: " + mStarted);
+ }
+ }
+
public interface PowerData {
@NonNull
String getUniqueDisplayId();
@@ -279,8 +456,12 @@
@VisibleForTesting
static class Injector {
- PmicMonitor getPmicMonitor(PowerChangeListener listener, int pollingTime) {
- return new PmicMonitor(listener, pollingTime);
+ PmicMonitor getPmicMonitor(PowerChangeListener powerChangeListener,
+ IThermalService thermalService,
+ int pollingMaxTimeMillis,
+ int pollingMinTimeMillis) {
+ return new PmicMonitor(powerChangeListener, thermalService, pollingMaxTimeMillis,
+ pollingMinTimeMillis);
}
DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
similarity index 77%
rename from services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
rename to services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
index 4498258..21ef309 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
@@ -22,6 +22,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.IThermalService;
@@ -33,8 +35,10 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DeviceConfigParsingUtils;
@@ -43,12 +47,15 @@
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
-class BrightnessThermalClamper extends
- BrightnessClamper<BrightnessThermalClamper.ThermalData> {
+class BrightnessThermalModifier implements BrightnessStateModifier,
+ BrightnessClamperController.DisplayDeviceDataListener,
+ BrightnessClamperController.StatefulModifier,
+ BrightnessClamperController.DeviceConfigListener {
private static final String TAG = "BrightnessThermalClamper";
@NonNull
@@ -58,6 +65,11 @@
// data from DeviceConfig, for all displays, for all dataSets
// mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
@NonNull
+ protected final Handler mHandler;
+ @NonNull
+ protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
+
+ @NonNull
private Map<String, Map<String, ThermalBrightnessThrottlingData>>
mThermalThrottlingDataOverride = Map.of();
// data from DisplayDeviceConfig, for particular display+dataSet
@@ -73,6 +85,8 @@
private String mDataId = null;
@Temperature.ThrottlingStatus
private int mThrottlingStatus = Temperature.THROTTLING_NONE;
+ private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ private boolean mApplied = false;
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
try {
@@ -88,53 +102,51 @@
mDataSetMapper = ThermalBrightnessThrottlingData::create;
- BrightnessThermalClamper(Handler handler, ClamperChangeListener listener,
- ThermalData thermalData) {
- this(new Injector(), handler, listener, thermalData);
+ BrightnessThermalModifier(Handler handler, ClamperChangeListener listener,
+ BrightnessClamperController.DisplayDeviceData data) {
+ this(new Injector(), handler, listener, data);
}
@VisibleForTesting
- BrightnessThermalClamper(Injector injector, Handler handler,
- ClamperChangeListener listener, ThermalData thermalData) {
- super(handler, listener);
+ BrightnessThermalModifier(Injector injector, @NonNull Handler handler,
+ @NonNull ClamperChangeListener listener,
+ @NonNull BrightnessClamperController.DisplayDeviceData data) {
+ mHandler = handler;
+ mChangeListener = listener;
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mThermalStatusObserver = new ThermalStatusObserver(injector, handler);
mHandler.post(() -> {
- setDisplayData(thermalData);
- loadOverrideData();
- });
-
- }
-
- @Override
- @NonNull
- Type getType() {
- return Type.THERMAL;
- }
-
- @Override
- void onDeviceConfigChanged() {
- mHandler.post(() -> {
- loadOverrideData();
- recalculateActiveData();
- });
- }
-
- @Override
- void onDisplayChanged(ThermalData data) {
- mHandler.post(() -> {
setDisplayData(data);
- recalculateActiveData();
+ loadOverrideData();
});
}
+ //region BrightnessStateModifier
+ @Override
+ public void apply(DisplayManagerInternal.DisplayPowerRequest request,
+ DisplayBrightnessState.Builder stateBuilder) {
+ if (stateBuilder.getMaxBrightness() > mBrightnessCap) {
+ stateBuilder.setMaxBrightness(mBrightnessCap);
+ stateBuilder.setBrightness(Math.min(stateBuilder.getBrightness(), mBrightnessCap));
+ stateBuilder.setBrightnessMaxReason(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL);
+ stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
+ // set fast change only when modifier is activated.
+ // this will allow auto brightness to apply slow change even when modifier is active
+ if (!mApplied) {
+ stateBuilder.setIsSlowChange(false);
+ }
+ mApplied = true;
+ } else {
+ mApplied = false;
+ }
+ }
@Override
- void stop() {
+ public void stop() {
mThermalStatusObserver.stopObserving();
}
@Override
- void dump(PrintWriter writer) {
+ public void dump(PrintWriter writer) {
writer.println("BrightnessThermalClamper:");
writer.println(" mThrottlingStatus: " + mThrottlingStatus);
writer.println(" mUniqueDisplayId: " + mUniqueDisplayId);
@@ -142,10 +154,53 @@
writer.println(" mDataOverride: " + mThermalThrottlingDataOverride);
writer.println(" mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
writer.println(" mDataActive: " + mThermalThrottlingDataActive);
+ writer.println(" mBrightnessCap:" + mBrightnessCap);
+ writer.println(" mApplied:" + mApplied);
mThermalStatusObserver.dump(writer);
- super.dump(writer);
}
+ @Override
+ public boolean shouldListenToLightSensor() {
+ return false;
+ }
+
+ @Override
+ public void setAmbientLux(float lux) {
+ // noop
+ }
+ //endregion
+
+ //region DisplayDeviceDataListener
+ @Override
+ public void onDisplayChanged(BrightnessClamperController.DisplayDeviceData data) {
+ mHandler.post(() -> {
+ setDisplayData(data);
+ recalculateActiveData();
+ });
+ }
+ //endregion
+
+ //region StatefulModifier
+ @Override
+ public void applyStateChange(
+ BrightnessClamperController.ModifiersAggregatedState aggregatedState) {
+ if (aggregatedState.mMaxBrightness > mBrightnessCap) {
+ aggregatedState.mMaxBrightness = mBrightnessCap;
+ aggregatedState.mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
+ }
+ }
+ //endregion
+
+ //region DeviceConfigListener
+ @Override
+ public void onDeviceConfigChanged() {
+ mHandler.post(() -> {
+ loadOverrideData();
+ recalculateActiveData();
+ });
+ }
+ //endregion
+
private void recalculateActiveData() {
if (mUniqueDisplayId == null || mDataId == null) {
return;
@@ -176,14 +231,11 @@
private void recalculateBrightnessCap() {
float brightnessCap = PowerManager.BRIGHTNESS_MAX;
- boolean isActive = false;
-
if (mThermalThrottlingDataActive != null) {
// Throttling levels are sorted by increasing severity
for (ThrottlingLevel level : mThermalThrottlingDataActive.throttlingLevels) {
if (level.thermalStatus <= mThrottlingStatus) {
brightnessCap = level.brightness;
- isActive = true;
} else {
// Throttling levels that are greater than the current status are irrelevant
break;
@@ -191,9 +243,8 @@
}
}
- if (brightnessCap != mBrightnessCap || mIsActive != isActive) {
+ if (brightnessCap != mBrightnessCap) {
mBrightnessCap = brightnessCap;
- mIsActive = isActive;
mChangeListener.onChanged();
}
}
@@ -205,7 +256,6 @@
}
}
-
private final class ThermalStatusObserver extends IThermalEventListener.Stub {
private final Injector mInjector;
private final Handler mHandler;
@@ -228,7 +278,7 @@
String curType = mObserverTempSensor.type;
mObserverTempSensor = tempSensor;
- if (curType.equals(tempSensor.type)) {
+ if (Objects.equals(curType, tempSensor.type)) {
Slog.d(TAG, "Thermal status observer already started");
return;
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java b/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
index 26784f23..355f4fe 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
@@ -18,15 +18,12 @@
import static com.android.server.display.brightness.clamper.BrightnessPowerClamper.PowerChangeListener;
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.IThermalService;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.Temperature;
import android.power.PowerStatsInternal;
import android.util.IntArray;
@@ -51,25 +48,30 @@
// The executor to periodically monitor the display power.
private final ScheduledExecutorService mExecutor;
- @NonNull
- private final PowerChangeListener mPowerChangeListener;
- private final long mPowerMonitorPeriodConfigSecs;
+ private final long mPowerMonitorPeriodConfigMillis;
private final PowerStatsInternal mPowerStatsInternal;
@VisibleForTesting final IThermalService mThermalService;
+ @VisibleForTesting PowerChangeListener mPowerChangeListener;
private ScheduledFuture<?> mPmicMonitorFuture;
private float mLastEnergyConsumed = 0;
- private float mCurrentAvgPower = 0;
+ private float mCurrentTotalAvgPower = 0;
private Temperature mCurrentTemperature;
private long mCurrentTimestampMillis = 0;
+ private float[] mAvgPowerCircularList;
+ private int mPowerListStart = 0;
+ private int mPowerListEnd = 0;
- PmicMonitor(PowerChangeListener listener, int powerMonitorPeriodConfigSecs) {
+ PmicMonitor(PowerChangeListener listener,
+ IThermalService thermalService,
+ int pollingMaxTimeMillis,
+ int pollingMinTimeMillis) {
mPowerChangeListener = listener;
+ mThermalService = thermalService;
mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
+ mAvgPowerCircularList = new float[pollingMaxTimeMillis / pollingMinTimeMillis];
// start a periodic worker thread.
mExecutor = Executors.newSingleThreadScheduledExecutor();
- mPowerMonitorPeriodConfigSecs = (long) powerMonitorPeriodConfigSecs;
+ mPowerMonitorPeriodConfigMillis = pollingMinTimeMillis;
}
@Nullable
@@ -141,12 +143,28 @@
// capture thermal state.
Temperature displayTemperature = getDisplayTemperature();
- mCurrentAvgPower = currentPower;
+ boolean isBufferFull = false;
+ mAvgPowerCircularList[mPowerListEnd] = currentPower;
+ mCurrentTotalAvgPower += currentPower;
+ mPowerListEnd =
+ (mPowerListEnd + 1) % mAvgPowerCircularList.length;
+ if (mPowerListStart == mPowerListEnd) {
+ isBufferFull = true;
+ }
+
mCurrentTemperature = displayTemperature;
mLastEnergyConsumed = displayResults[0].energyUWs;
mCurrentTimestampMillis = displayResults[0].timestampMs;
- if (mCurrentTemperature != null) {
- mPowerChangeListener.onChanged(mCurrentAvgPower, mCurrentTemperature.getStatus());
+
+ if (mCurrentTemperature != null && isBufferFull) {
+ mPowerChangeListener.onChanged(mCurrentTotalAvgPower / mAvgPowerCircularList.length,
+ mCurrentTemperature.getStatus());
+ }
+
+ // average power long-term list is full, reset values for next cycle.
+ if (isBufferFull) {
+ mCurrentTotalAvgPower = mCurrentTotalAvgPower - mAvgPowerCircularList[mPowerListStart];
+ mPowerListStart = (mPowerListStart + 1) % mAvgPowerCircularList.length;
}
}
@@ -165,11 +183,11 @@
if (mPmicMonitorFuture == null) {
mPmicMonitorFuture = mExecutor.scheduleAtFixedRate(
this::capturePeriodicDisplayPower,
- mPowerMonitorPeriodConfigSecs,
- mPowerMonitorPeriodConfigSecs,
- TimeUnit.SECONDS);
+ mPowerMonitorPeriodConfigMillis,
+ mPowerMonitorPeriodConfigMillis,
+ TimeUnit.MILLISECONDS);
} else {
- Slog.e(TAG, "already scheduled, stop() called before start.");
+ Slog.e(TAG, "PMIC already scheduled, stop() called before start.");
}
}
@@ -184,6 +202,23 @@
}
/**
+ * Updates PMIC configuration.
+ */
+ public void updateConfiguration() {
+ if (mPmicMonitorFuture != null) {
+ mPmicMonitorFuture.cancel(true);
+ mPmicMonitorFuture = null;
+ }
+ }
+
+ /**
+ * Returns if PMIC monitor is stopped.
+ */
+ public boolean isStopped() {
+ return mPmicMonitorFuture == null;
+ }
+
+ /**
* Shutdown power IC service and worker thread.
*/
public void shutdown() {
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
index 1db9bbe..91c985830 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
@@ -100,6 +100,7 @@
writer.println("AutoBrightnessFallbackStrategy:");
writer.println(" mLeadDisplayId=" + mLeadDisplayId);
writer.println(" mIsDisplayEnabled=" + mIsDisplayEnabled);
+ writer.println("");
if (mScreenOffBrightnessSensorController != null) {
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
mScreenOffBrightnessSensorController.dump(ipw);
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 35be0f3..69b67c8 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -420,6 +420,7 @@
*/
public void dump(PrintWriter pw) {
pw.println("DisplayManagerFlags:");
+ pw.println("--------------------");
pw.println(" " + mAdaptiveToneImprovements1);
pw.println(" " + mAdaptiveToneImprovements2);
pw.println(" " + mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState);
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index d909004..18e0d6e 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -578,7 +578,8 @@
* @param pw The stream to dump information to.
*/
public void dump(PrintWriter pw) {
- pw.println("DisplayModeDirector");
+ pw.println("DisplayModeDirector:");
+ pw.println("--------------------");
synchronized (mLock) {
pw.println(" mSupportedModesByDisplay:");
for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
diff --git a/services/core/java/com/android/server/display/state/DisplayStateController.java b/services/core/java/com/android/server/display/state/DisplayStateController.java
index 21bb208..dba6874 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -114,9 +114,9 @@
*
* @param pw The PrintWriter used to dump the state.
*/
- public void dumpsys(PrintWriter pw) {
- pw.println();
+ public void dump(PrintWriter pw) {
pw.println("DisplayStateController:");
+ pw.println("-----------------------");
pw.println(" mPerformScreenOffTransition:" + mPerformScreenOffTransition);
pw.println(" mDozeStateOverride=" + mDozeStateOverride);
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 7ea576d..49c45a7 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -427,7 +427,8 @@
* The writer used to dump the state.
*/
public void dump(PrintWriter writer) {
- writer.println("DisplayWhiteBalanceController");
+ writer.println("DisplayWhiteBalanceController:");
+ writer.println("------------------------------");
writer.println(" mLoggingEnabled=" + mLoggingEnabled);
writer.println(" mEnabled=" + mEnabled);
writer.println(" mStrongModeEnabled=" + mStrongModeEnabled);
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
index 0efb749..a5755ac 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
@@ -23,7 +23,6 @@
import android.os.Message;
import android.util.Slog;
-import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
@@ -129,7 +128,8 @@
* The writer used to dump the state.
*/
public void dump(PrintWriter writer) {
- writer.println("DisplayWhiteBalanceSettings");
+ writer.println("DisplayWhiteBalanceSettings:");
+ writer.println("----------------------------");
writer.println(" mLoggingEnabled=" + mLoggingEnabled);
writer.println(" mContext=" + mContext);
writer.println(" mHandler=" + mHandler);
diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
index 649678c..69ba785 100644
--- a/services/core/java/com/android/server/flags/services.aconfig
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -56,3 +56,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "backstage_power"
+ name: "trace_battery_changed_broadcast_event"
+ description: "Add tracing to record battery changed broadcast event"
+ bug: "365410144"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 234d6d3..236333e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -53,6 +53,10 @@
// Default state used in common by all the feature actions.
protected static final int STATE_NONE = 0;
+ // Delay to query avr's audio status, some avrs could report the old volume first. It could
+ // show inverse mute state on TV.
+ protected static final long DELAY_GIVE_AUDIO_STATUS = 500;
+
// Internal state indicating the progress of action.
protected int mState = STATE_NONE;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 81204ef..a8d5696 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -927,12 +927,14 @@
protected int handleVendorCommandWithId(HdmiCecMessage message) {
byte[] params = message.getParams();
int vendorId = HdmiUtils.threeBytesToInt(params);
- if (message.getDestination() == Constants.ADDR_BROADCAST
- || message.getSource() == Constants.ADDR_UNREGISTERED) {
- Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
- } else if (!mService.invokeVendorCommandListenersOnReceived(
+ if (!mService.invokeVendorCommandListenersOnReceived(
mDeviceType, message.getSource(), message.getDestination(), params, true)) {
- return Constants.ABORT_REFUSED;
+ if (message.getDestination() == Constants.ADDR_BROADCAST
+ || message.getSource() == Constants.ADDR_UNREGISTERED) {
+ Slog.v(TAG, "Broadcast vendor command with no listeners. Ignoring");
+ } else {
+ return Constants.ABORT_REFUSED;
+ }
}
return Constants.HANDLED;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 81c30dd..101596d 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);
}
@@ -1142,6 +1150,13 @@
return Constants.ABORT_REFUSED;
}
+ if (mArcEstablished) {
+ HdmiLogger.debug("ARC is already established.");
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildReportArcInitiated(
+ getDeviceInfo().getLogicalAddress(), message.getSource());
+ mService.sendCecCommand(command);
+ return Constants.HANDLED;
+ }
// In case where <Initiate Arc> is started by <Request ARC Initiation>, this message is
// handled in RequestArcInitiationAction as well.
SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
@@ -1509,7 +1524,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/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3c7b9d3..271836a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1638,6 +1638,10 @@
mHandler.post(new WorkSourceUidPreservingRunnable(runnable));
}
+ void runOnServiceThreadDelayed(Runnable runnable, long delay) {
+ mHandler.postDelayed(new WorkSourceUidPreservingRunnable(runnable), delay);
+ }
+
private void assertRunOnServiceThread() {
if (Looper.myLooper() != mHandler.getLooper()) {
throw new IllegalStateException("Should run on service thread.");
diff --git a/services/core/java/com/android/server/hdmi/RequestSadAction.java b/services/core/java/com/android/server/hdmi/RequestSadAction.java
index 0188e96..2566300 100644
--- a/services/core/java/com/android/server/hdmi/RequestSadAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestSadAction.java
@@ -19,6 +19,8 @@
import android.hardware.hdmi.HdmiControlManager;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -36,7 +38,8 @@
// State in which the action is waiting for <Report Short Audio Descriptor>.
private static final int STATE_WAITING_FOR_REPORT_SAD = 1;
private static final int MAX_SAD_PER_REQUEST = 4;
- private static final int RETRY_COUNTER_MAX = 1;
+ @VisibleForTesting
+ public static final int RETRY_COUNTER_MAX = 3;
private final int mTargetAddress;
private final RequestSadCallback mCallback;
private final List<Integer> mCecCodecsToQuery = new ArrayList<>();
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/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 7e18d84..11682f6 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -180,15 +180,22 @@
&& localDevice().getService().isAbsoluteVolumeBehaviorEnabled()) {
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTargetAddress),
- __ -> sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
- getSourceAddress(),
- localDevice().findAudioReceiverAddress())));
+ __ -> queryAvrAudioStatus());
} else {
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTargetAddress));
}
}
+ private void queryAvrAudioStatus() {
+ localDevice().mService.runOnServiceThreadDelayed(
+ () -> sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
+ getSourceAddress(),
+ localDevice().findAudioReceiverAddress())),
+ DELAY_GIVE_AUDIO_STATUS);
+
+ }
+
@Override
public boolean processCommand(HdmiCecMessage cmd) {
// Send key action doesn't need any incoming CEC command, hence does not consume it.
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 5ab22e1..e6abcb9 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -60,6 +60,12 @@
boolean start() {
// Seq #37.
if (mEnabled) {
+ // Avoid triggering duplicate RequestSadAction events.
+ // This could lead to unexpected responses from the AVR and cause the TV to receive data
+ // out of order. The SAD report does not provide information about the order of events.
+ if ((tv().hasAction(RequestSadAction.class))) {
+ return true;
+ }
// Request SADs before enabling ARC
RequestSadAction action = new RequestSadAction(
localDevice(), Constants.ADDR_AUDIO_SYSTEM,
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 1220542..dfcea9f 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();
@@ -597,9 +597,6 @@
mKeyRemapper.systemRunning();
mPointerIconCache.systemRunning();
mKeyboardGlyphManager.systemRunning();
- if (mTouchpadDebugViewController != null) {
- mTouchpadDebugViewController.systemRunning();
- }
}
private void reloadDeviceAliases() {
@@ -2272,13 +2269,9 @@
// Native callback.
@SuppressWarnings("unused")
private void notifyTouchpadHardwareState(TouchpadHardwareState hardwareStates, int deviceId) {
- // TODO(b/286551975): sent the touchpad hardware state data here to TouchpadDebugActivity
- Slog.d(TAG, "notifyTouchpadHardwareState: Time: "
- + hardwareStates.getTimestamp() + ", No. Buttons: "
- + hardwareStates.getButtonsDown() + ", No. Fingers: "
- + hardwareStates.getFingerCount() + ", No. Touch: "
- + hardwareStates.getTouchCount() + ", Id: "
- + deviceId);
+ if (mTouchpadDebugViewController != null) {
+ mTouchpadDebugViewController.updateTouchpadHardwareState(hardwareStates, deviceId);
+ }
}
// Native callback.
@@ -2743,21 +2736,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());
@@ -3340,6 +3360,13 @@
}
}
+ void updateTouchpadVisualizerEnabled(boolean enabled) {
+ mNative.setShouldNotifyTouchpadHardwareState(enabled);
+ if (mTouchpadDebugViewController != null) {
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(enabled);
+ }
+ }
+
void updatePointerLocationEnabled(boolean enabled) {
mWindowManagerCallbacks.notifyPointerLocationChanged(enabled);
}
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index ef61d02..835fb72 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -180,7 +180,7 @@
}
private void updateTouchpadHardwareStateNotificationsEnabled() {
- mNative.setShouldNotifyTouchpadHardwareState(InputSettings.useTouchpadVisualizer(mContext));
+ mService.updateTouchpadVisualizerEnabled(InputSettings.useTouchpadVisualizer(mContext));
}
private void updateTouchpadRightClickZoneEnabled() {
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
index 5fca771..486d4af 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
@@ -16,56 +16,241 @@
package com.android.server.input.debug;
+import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.hardware.input.InputManager;
+import android.util.Slog;
import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
-public class TouchpadDebugView extends LinearLayout {
+import com.android.server.input.TouchpadFingerState;
+import com.android.server.input.TouchpadHardwareProperties;
+import com.android.server.input.TouchpadHardwareState;
+import java.util.Objects;
+
+public class TouchpadDebugView extends LinearLayout {
/**
* Input device ID for the touchpad that this debug view is displaying.
*/
private final int mTouchpadId;
- public TouchpadDebugView(Context context, int touchpadId) {
+ @NonNull
+ private final WindowManager mWindowManager;
+
+ @NonNull
+ private final WindowManager.LayoutParams mWindowLayoutParams;
+
+ private final int mTouchSlop;
+
+ private float mTouchDownX;
+ private float mTouchDownY;
+ private int mScreenWidth;
+ private int mScreenHeight;
+ private int mWindowLocationBeforeDragX;
+ private int mWindowLocationBeforeDragY;
+ @NonNull
+ private TouchpadHardwareState mLastTouchpadState =
+ new TouchpadHardwareState(0, 0 /* buttonsDown */, 0, 0,
+ new TouchpadFingerState[0]);
+ private TouchpadVisualizationView mTouchpadVisualizationView;
+
+ public TouchpadDebugView(Context context, int touchpadId,
+ TouchpadHardwareProperties touchpadHardwareProperties) {
super(context);
mTouchpadId = touchpadId;
- init(context);
+ mWindowManager =
+ Objects.requireNonNull(getContext().getSystemService(WindowManager.class));
+ init(context, touchpadHardwareProperties, touchpadId);
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+
+ // TODO(b/360137366): Use the hardware properties to initialise layout parameters.
+ mWindowLayoutParams = new WindowManager.LayoutParams();
+ mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ mWindowLayoutParams.privateFlags |=
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ mWindowLayoutParams.setFitInsetsTypes(0);
+ mWindowLayoutParams.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mWindowLayoutParams.format = PixelFormat.TRANSLUCENT;
+ mWindowLayoutParams.setTitle("TouchpadDebugView - display " + mContext.getDisplayId());
+
+ mWindowLayoutParams.x = 40;
+ mWindowLayoutParams.y = 100;
+ mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
}
- private void init(Context context) {
+ private void init(Context context, TouchpadHardwareProperties touchpadHardwareProperties,
+ int touchpadId) {
setOrientation(VERTICAL);
- setLayoutParams(new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT));
+ setLayoutParams(new LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT));
setBackgroundColor(Color.TRANSPARENT);
- // TODO(b/286551975): Replace this content with the touchpad debug view.
+ TextView nameView = new TextView(context);
+ nameView.setBackgroundColor(Color.RED);
+ nameView.setTextSize(20);
+ nameView.setText(Objects.requireNonNull(Objects.requireNonNull(
+ mContext.getSystemService(InputManager.class))
+ .getInputDevice(touchpadId)).getName());
+ nameView.setGravity(Gravity.CENTER);
+ nameView.setTextColor(Color.WHITE);
+ nameView.setLayoutParams(new LayoutParams(1000, 200));
- TextView textView1 = new TextView(context);
- textView1.setBackgroundColor(Color.parseColor("#FFFF0000"));
- textView1.setTextSize(20);
- textView1.setText("Touchpad Debug View 1");
- textView1.setGravity(Gravity.CENTER);
- textView1.setTextColor(Color.WHITE);
+ mTouchpadVisualizationView = new TouchpadVisualizationView(context,
+ touchpadHardwareProperties);
+ mTouchpadVisualizationView.setBackgroundColor(Color.WHITE);
+ //TODO(b/365568238): set the view size according to the touchpad size from the
+ // TouchpadHardwareProperties
+ mTouchpadVisualizationView.setLayoutParams(new LayoutParams(778, 500));
- textView1.setLayoutParams(new LayoutParams(1000, 200));
+ //TODO(b/365562952): Add a display for recognized gesture info here
+ TextView gestureInfoView = new TextView(context);
+ gestureInfoView.setBackgroundColor(Color.GRAY);
+ gestureInfoView.setTextSize(20);
+ gestureInfoView.setText("Touchpad Debug View 3");
+ gestureInfoView.setGravity(Gravity.CENTER);
+ gestureInfoView.setTextColor(Color.BLACK);
+ gestureInfoView.setLayoutParams(new LayoutParams(1000, 200));
- TextView textView2 = new TextView(context);
- textView2.setBackgroundColor(Color.BLUE);
- textView2.setTextSize(20);
- textView2.setText("Touchpad Debug View 2");
- textView2.setGravity(Gravity.CENTER);
- textView2.setTextColor(Color.WHITE);
- textView2.setLayoutParams(new LayoutParams(1000, 200));
+ addView(nameView);
+ addView(mTouchpadVisualizationView);
+ addView(gestureInfoView);
- addView(textView1);
- addView(textView2);
+ updateScreenDimensions();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ float deltaX;
+ float deltaY;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mWindowLocationBeforeDragX = mWindowLayoutParams.x;
+ mWindowLocationBeforeDragY = mWindowLayoutParams.y;
+ mTouchDownX = event.getRawX() - mWindowLocationBeforeDragX;
+ mTouchDownY = event.getRawY() - mWindowLocationBeforeDragY;
+ return true;
+
+ case MotionEvent.ACTION_MOVE:
+ deltaX = event.getRawX() - mWindowLayoutParams.x - mTouchDownX;
+ deltaY = event.getRawY() - mWindowLayoutParams.y - mTouchDownY;
+ if (isSlopExceeded(deltaX, deltaY)) {
+ mWindowLayoutParams.x =
+ Math.max(0, Math.min((int) (event.getRawX() - mTouchDownX),
+ mScreenWidth - this.getWidth()));
+ mWindowLayoutParams.y =
+ Math.max(0, Math.min((int) (event.getRawY() - mTouchDownY),
+ mScreenHeight - this.getHeight()));
+
+ mWindowManager.updateViewLayout(this, mWindowLayoutParams);
+ }
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ deltaX = event.getRawX() - mWindowLayoutParams.x - mTouchDownX;
+ deltaY = event.getRawY() - mWindowLayoutParams.y - mTouchDownY;
+ if (!isSlopExceeded(deltaX, deltaY)) {
+ performClick();
+ }
+ return true;
+
+ case MotionEvent.ACTION_CANCEL:
+ // Move the window back to the original position
+ mWindowLayoutParams.x = mWindowLocationBeforeDragX;
+ mWindowLayoutParams.y = mWindowLocationBeforeDragY;
+ mWindowManager.updateViewLayout(this, mWindowLayoutParams);
+ return true;
+
+ default:
+ return super.onTouchEvent(event);
+ }
+ }
+
+ @Override
+ public boolean performClick() {
+ super.performClick();
+ Slog.d("TouchpadDebugView", "You tapped the window!");
+ return true;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateScreenDimensions();
+
+ // Adjust view position to stay within screen bounds after rotation
+ mWindowLayoutParams.x =
+ Math.max(0, Math.min(mWindowLayoutParams.x, mScreenWidth - getWidth()));
+ mWindowLayoutParams.y =
+ Math.max(0, Math.min(mWindowLayoutParams.y, mScreenHeight - getHeight()));
+ mWindowManager.updateViewLayout(this, mWindowLayoutParams);
+ }
+
+ private boolean isSlopExceeded(float deltaX, float deltaY) {
+ return deltaX * deltaX + deltaY * deltaY >= mTouchSlop * mTouchSlop;
+ }
+
+ private void updateScreenDimensions() {
+ Rect windowBounds =
+ mWindowManager.getCurrentWindowMetrics().getBounds();
+ mScreenWidth = windowBounds.width();
+ mScreenHeight = windowBounds.height();
}
public int getTouchpadId() {
return mTouchpadId;
}
+
+ public WindowManager.LayoutParams getWindowLayoutParams() {
+ return mWindowLayoutParams;
+ }
+
+ /**
+ * Notify the view of a change in the hardware state of a touchpad. The view should
+ * update its content to reflect the new state.
+ *
+ * @param touchpadHardwareState the hardware state of a touchpad
+ * @param deviceId the deviceId of the touchpad that is sending the hardware state
+ */
+ public void updateHardwareState(TouchpadHardwareState touchpadHardwareState, int deviceId) {
+ if (deviceId != mTouchpadId) {
+ return;
+ }
+
+ mTouchpadVisualizationView.onTouchpadHardwareStateNotified(touchpadHardwareState);
+ if (mLastTouchpadState.getButtonsDown() == 0) {
+ if (touchpadHardwareState.getButtonsDown() > 0) {
+ onTouchpadButtonPress();
+ }
+ } else {
+ if (touchpadHardwareState.getButtonsDown() == 0) {
+ onTouchpadButtonRelease();
+ }
+ }
+ mLastTouchpadState = touchpadHardwareState;
+ }
+
+ private void onTouchpadButtonPress() {
+ Slog.d("TouchpadDebugView", "You clicked me!");
+ getChildAt(0).setBackgroundColor(Color.BLUE);
+ }
+
+ private void onTouchpadButtonRelease() {
+ Slog.d("TouchpadDebugView", "You released the click");
+ getChildAt(0).setBackgroundColor(Color.RED);
+ }
}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
index c7760c6..b4b357a 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
@@ -18,74 +18,92 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Slog;
-import android.view.Display;
-import android.view.Gravity;
import android.view.InputDevice;
import android.view.WindowManager;
import com.android.server.input.InputManagerService;
import com.android.server.input.TouchpadHardwareProperties;
+import com.android.server.input.TouchpadHardwareState;
import java.util.Objects;
-public class TouchpadDebugViewController {
+public class TouchpadDebugViewController implements InputManager.InputDeviceListener {
- private static final String TAG = "TouchpadDebugViewController";
+ private static final String TAG = "TouchpadDebugView";
private final Context mContext;
private final Handler mHandler;
+
@Nullable
private TouchpadDebugView mTouchpadDebugView;
+
private final InputManagerService mInputManagerService;
+ private boolean mTouchpadVisualizerEnabled = false;
public TouchpadDebugViewController(Context context, Looper looper,
- InputManagerService inputManagerService) {
- final DisplayManager displayManager = Objects.requireNonNull(
- context.getSystemService(DisplayManager.class));
- final Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
- mContext = context.createDisplayContext(defaultDisplay);
+ InputManagerService inputManagerService) {
+ //TODO(b/363979581): Handle multi-display scenarios
+ mContext = context;
mHandler = new Handler(looper);
mInputManagerService = inputManagerService;
}
- public void systemRunning() {
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
final InputManager inputManager = Objects.requireNonNull(
mContext.getSystemService(InputManager.class));
- inputManager.registerInputDeviceListener(mInputDeviceListener, mHandler);
- for (int deviceId : inputManager.getInputDeviceIds()) {
- mInputDeviceListener.onInputDeviceAdded(deviceId);
+ InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+
+ if (Objects.requireNonNull(inputDevice).supportsSource(
+ InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)
+ && mTouchpadVisualizerEnabled) {
+ showDebugView(deviceId);
}
}
- private final InputManager.InputDeviceListener mInputDeviceListener =
- new InputManager.InputDeviceListener() {
- @Override
- public void onInputDeviceAdded(int deviceId) {
- final InputManager inputManager = Objects.requireNonNull(
- mContext.getSystemService(InputManager.class));
- InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ hideDebugView(deviceId);
+ if (mTouchpadDebugView == null) {
+ final InputManager inputManager = Objects.requireNonNull(
+ mContext.getSystemService(InputManager.class));
+ for (int id : inputManager.getInputDeviceIds()) {
+ onInputDeviceAdded(id);
+ }
+ }
+ }
- if (Objects.requireNonNull(inputDevice).supportsSource(
- InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)) {
- showDebugView(deviceId);
- }
- }
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ }
- @Override
- public void onInputDeviceRemoved(int deviceId) {
- hideDebugView(deviceId);
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId) {
- }
- };
+ /**
+ * Notify the controller that the touchpad visualizer setting value has changed.
+ * This must be called from the same looper thread as {@code mHandler}.
+ */
+ public void updateTouchpadVisualizerEnabled(boolean touchpadVisualizerEnabled) {
+ if (mTouchpadVisualizerEnabled == touchpadVisualizerEnabled) {
+ return;
+ }
+ mTouchpadVisualizerEnabled = touchpadVisualizerEnabled;
+ final InputManager inputManager = Objects.requireNonNull(
+ mContext.getSystemService(InputManager.class));
+ if (touchpadVisualizerEnabled) {
+ inputManager.registerInputDeviceListener(this, mHandler);
+ for (int deviceId : inputManager.getInputDeviceIds()) {
+ onInputDeviceAdded(deviceId);
+ }
+ } else {
+ if (mTouchpadDebugView != null) {
+ hideDebugView(mTouchpadDebugView.getTouchpadId());
+ }
+ inputManager.unregisterInputDeviceListener(this);
+ }
+ }
private void showDebugView(int touchpadId) {
if (mTouchpadDebugView != null) {
@@ -94,35 +112,20 @@
final WindowManager wm = Objects.requireNonNull(
mContext.getSystemService(WindowManager.class));
- mTouchpadDebugView = new TouchpadDebugView(mContext, touchpadId);
-
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
- lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
- lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- lp.setFitInsetsTypes(0);
- lp.layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- lp.format = PixelFormat.TRANSLUCENT;
- lp.setTitle("TouchpadDebugView - display " + mContext.getDisplayId());
- lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
-
- lp.x = 40;
- lp.y = 100;
- lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
- lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
- lp.gravity = Gravity.TOP | Gravity.LEFT;
-
- wm.addView(mTouchpadDebugView, lp);
- Slog.d(TAG, "Touchpad debug view created.");
-
- TouchpadHardwareProperties mTouchpadHardwareProperties =
+ TouchpadHardwareProperties touchpadHardwareProperties =
mInputManagerService.getTouchpadHardwareProperties(
touchpadId);
- // TODO(b/360137366): Use the hardware properties to initialise layout parameters.
- if (mTouchpadHardwareProperties != null) {
- Slog.d(TAG, mTouchpadHardwareProperties.toString());
+
+ mTouchpadDebugView = new TouchpadDebugView(mContext, touchpadId,
+ touchpadHardwareProperties);
+ final WindowManager.LayoutParams mWindowLayoutParams =
+ mTouchpadDebugView.getWindowLayoutParams();
+
+ wm.addView(mTouchpadDebugView, mWindowLayoutParams);
+ Slog.d(TAG, "Touchpad debug view created.");
+
+ if (touchpadHardwareProperties != null) {
+ Slog.d(TAG, touchpadHardwareProperties.toString());
} else {
Slog.w(TAG, "Failed to retrieve touchpad hardware properties for "
+ "device ID: " + touchpadId);
@@ -139,4 +142,17 @@
mTouchpadDebugView = null;
Slog.d(TAG, "Touchpad debug view removed.");
}
+
+ /**
+ * Notifies about an update in the touchpad's hardware state.
+ *
+ * @param touchpadHardwareState the hardware state of a touchpad
+ * @param deviceId the deviceId of the touchpad that is sending the hardware state
+ */
+ public void updateTouchpadHardwareState(TouchpadHardwareState touchpadHardwareState,
+ int deviceId) {
+ if (mTouchpadDebugView != null) {
+ mTouchpadDebugView.updateHardwareState(touchpadHardwareState, deviceId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadVisualizationView.java b/services/core/java/com/android/server/input/debug/TouchpadVisualizationView.java
new file mode 100644
index 0000000..9ba7d0a
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/TouchpadVisualizationView.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 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.server.input.debug;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.Slog;
+import android.view.View;
+
+import com.android.server.input.TouchpadFingerState;
+import com.android.server.input.TouchpadHardwareProperties;
+import com.android.server.input.TouchpadHardwareState;
+
+public class TouchpadVisualizationView extends View {
+ private static final String TAG = "TouchpadVizMain";
+ private static final boolean DEBUG = true;
+
+ private final TouchpadHardwareProperties mTouchpadHardwareProperties;
+
+ TouchpadHardwareState mLatestHardwareState = new TouchpadHardwareState(0, 0, 0, 0,
+ new TouchpadFingerState[]{});
+
+ private final Paint mOvalPaint;
+
+ public TouchpadVisualizationView(Context context,
+ TouchpadHardwareProperties touchpadHardwareProperties) {
+ super(context);
+ mTouchpadHardwareProperties = touchpadHardwareProperties;
+ mOvalPaint = new Paint();
+ mOvalPaint.setAntiAlias(true);
+ mOvalPaint.setARGB(255, 0, 0, 0);
+ mOvalPaint.setStyle(Paint.Style.STROKE);
+ }
+
+ private final RectF mOvalRect = new RectF();
+
+ private void drawOval(Canvas canvas, float x, float y, float major, float minor, float angle,
+ Paint paint) {
+ canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.rotate(angle, x, y);
+ mOvalRect.left = x - minor / 2;
+ mOvalRect.right = x + minor / 2;
+ mOvalRect.top = y - major / 2;
+ mOvalRect.bottom = y + major / 2;
+ canvas.drawOval(mOvalRect, paint);
+ canvas.restore();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ for (TouchpadFingerState touchpadFingerState : mLatestHardwareState.getFingerStates()) {
+ float newX = translateRange(mTouchpadHardwareProperties.getLeft(),
+ mTouchpadHardwareProperties.getRight(), 0, getWidth(),
+ touchpadFingerState.getPositionX());
+
+ float newY = translateRange(mTouchpadHardwareProperties.getTop(),
+ mTouchpadHardwareProperties.getBottom(), 0, getHeight(),
+ touchpadFingerState.getPositionY());
+
+ float newAngle = -translateRange(mTouchpadHardwareProperties.getOrientationMinimum(),
+ mTouchpadHardwareProperties.getOrientationMaximum(), 0, 360,
+ touchpadFingerState.getOrientation());
+
+ float newTouchMajor =
+ touchpadFingerState.getTouchMajor() / mTouchpadHardwareProperties.getResX();
+ float newTouchMinor =
+ touchpadFingerState.getTouchMinor() / mTouchpadHardwareProperties.getResY();
+
+ drawOval(canvas, newX, newY, newTouchMajor, newTouchMinor, newAngle, mOvalPaint);
+ }
+ }
+
+ /**
+ * Receiving the touchpad hardware state and based on it update the latest hardware state.
+ *
+ * @param schs The new hardware state received.
+ */
+ public void onTouchpadHardwareStateNotified(TouchpadHardwareState schs) {
+ if (DEBUG) {
+ logHardwareState(schs);
+ }
+
+ mLatestHardwareState = schs;
+
+ invalidate();
+ }
+
+ private float translateRange(float rangeBeforeMin, float rangeBeforeMax,
+ float rangeAfterMin, float rangeAfterMax, float value) {
+ return rangeAfterMin + (value - rangeBeforeMin) / (rangeBeforeMax - rangeBeforeMin) * (
+ rangeAfterMax - rangeAfterMin);
+ }
+
+ private void logHardwareState(TouchpadHardwareState schs) {
+ Slog.d(TAG, "notifyTouchpadHardwareState: Time: "
+ + schs.getTimestamp() + ", No. Buttons: "
+ + schs.getButtonsDown() + ", No. Fingers: "
+ + schs.getFingerCount() + ", No. Touch: "
+ + schs.getTouchCount());
+
+ for (TouchpadFingerState finger : schs.getFingerStates()) {
+ Slog.d(TAG, "Finger #" + finger.getTrackingId()
+ + ": touchMajor= " + finger.getTouchMajor()
+ + ", touchMinor= " + finger.getTouchMinor()
+ + ", widthMajor= " + finger.getWidthMajor()
+ + ", widthMinor= " + finger.getWidthMinor()
+ + ", pressure= " + finger.getPressure()
+ + ", orientation= " + finger.getOrientation()
+ + ", positionX= " + finger.getPositionX()
+ + ", positionY= " + finger.getPositionY());
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index 58e3452..e1f26d6 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -116,11 +116,11 @@
boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason);
+ @SoftInputShowHideReason int reason, boolean async);
boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason);
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async);
@PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
void hideSoftInputFromServerForTest();
@@ -132,7 +132,8 @@
@Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod);
InputBindResult startInputOrWindowGainedFocus(
@StartInputReason int startInputReason, IInputMethodClient client,
@@ -290,17 +291,17 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
return mCallback.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
- resultReceiver, reason);
+ resultReceiver, reason, async);
}
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
return mCallback.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
- reason);
+ reason, async);
}
@EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
@@ -336,11 +337,13 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
mCallback.startInputOrWindowGainedFocusAsync(
startInputReason, client, windowToken, startInputFlags, softInputMode,
windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
- unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq);
+ unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq,
+ useAsyncShowHideMethod);
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2ad0d2a..f747879 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -350,13 +350,15 @@
@BinderThread
private int resolveImeUserIdLocked(@UserIdInt int callingProcessUserId,
@NonNull IInputMethodClient client) {
- if (mConcurrentMultiUserModeEnabled
- && callingProcessUserId == UserHandle.USER_SYSTEM) {
- final var clientState = mClientController.getClient(client.asBinder());
- return mUserManagerInternal.getUserAssignedToDisplay(
- clientState.mSelfReportedDisplayId);
+ if (mConcurrentMultiUserModeEnabled) {
+ if (callingProcessUserId == UserHandle.USER_SYSTEM) {
+ final var clientState = mClientController.getClient(client.asBinder());
+ return mUserManagerInternal.getUserAssignedToDisplay(
+ clientState.mSelfReportedDisplayId);
+ }
+ return callingProcessUserId;
}
- return callingProcessUserId;
+ return mCurrentImeUserId;
}
/**
@@ -3086,7 +3088,7 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#showSoftInput", mDumper);
@@ -3533,7 +3535,7 @@
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#hideSoftInput", mDumper);
synchronized (ImfLock.class) {
@@ -3675,7 +3677,8 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
// implemented by ZeroJankProxy
}
@@ -4675,6 +4678,7 @@
}
}
+ // TODO(b/356239178): Make dump proto multi-user aware.
private void dumpDebug(ProtoOutputStream proto, long fieldId) {
synchronized (ImfLock.class) {
final int userId = mCurrentImeUserId;
@@ -6100,17 +6104,40 @@
@BinderThread
private void dumpAsStringNoCheck(FileDescriptor fd, PrintWriter pw, String[] args,
boolean isCritical) {
+ final int argUserId = parseUserIdFromDumpArgs(args);
+ final Printer p = new PrintWriterPrinter(pw);
+ p.println("Current Input Method Manager state:");
+ p.println(" concurrentMultiUserModeEnabled=" + mConcurrentMultiUserModeEnabled);
+ if (mConcurrentMultiUserModeEnabled && argUserId == UserHandle.USER_NULL) {
+ mUserDataRepository.forAllUserData(
+ u -> dumpAsStringNoCheckForUser(u, fd, pw, args, isCritical));
+ } else {
+ final int userId = argUserId != UserHandle.USER_NULL ? argUserId : mCurrentImeUserId;
+ final var userData = getUserData(userId);
+ dumpAsStringNoCheckForUser(userData, fd, pw, args, isCritical);
+ }
+ }
+
+ @UserIdInt
+ private static int parseUserIdFromDumpArgs(String[] args) {
+ final int userIdx = Arrays.binarySearch(args, "--user");
+ if (userIdx == -1 || userIdx == args.length - 1) {
+ return UserHandle.USER_NULL;
+ }
+ return Integer.parseInt(args[userIdx + 1]);
+ }
+
+ // TODO(b/356239178): Update dump format output to better group per-user info.
+ @BinderThread
+ private void dumpAsStringNoCheckForUser(UserData userData, FileDescriptor fd, PrintWriter pw,
+ String[] args, boolean isCritical) {
+ final Printer p = new PrintWriterPrinter(pw);
IInputMethodInvoker method;
ClientState client;
-
- final Printer p = new PrintWriterPrinter(pw);
-
+ p.println(" UserId=" + userData.mUserId);
synchronized (ImfLock.class) {
- final int userId = mCurrentImeUserId;
- final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- final var userData = getUserData(userId);
- p.println("Current Input Method Manager state:");
- p.println(" concurrentMultiUserModeEnabled" + mConcurrentMultiUserModeEnabled);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(
+ userData.mUserId);
final List<InputMethodInfo> methodList = settings.getMethodList();
int numImes = methodList.size();
p.println(" Input Methods:");
@@ -6130,7 +6157,7 @@
p.println(" sessionRequested="
+ c.mSessionRequested);
p.println(" sessionRequestedForAccessibility="
- + c.mSessionRequestedForAccessibility);
+ + c.mSessionRequestedForAccessibility);
p.println(" curSession=" + c.mCurSession);
p.println(" selfReportedDisplayId=" + c.mSelfReportedDisplayId);
p.println(" uid=" + c.mUid);
@@ -6138,7 +6165,6 @@
};
mClientController.forAllClients(clientControllerDump);
final var bindingController = userData.mBindingController;
- p.println(" mCurrentImeUserId=" + userData.mUserId);
p.println(" mCurMethodId=" + bindingController.getSelectedMethodId());
client = userData.mCurClient;
p.println(" mCurClient=" + client + " mCurSeq="
@@ -6231,8 +6257,6 @@
p.println("No input method client.");
}
synchronized (ImfLock.class) {
- final int userId = mCurrentImeUserId;
- final var userData = getUserData(userId);
if (userData.mImeBindingState.mFocusedWindowClient != null
&& client != userData.mImeBindingState.mFocusedWindowClient) {
p.println(" ");
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index c940a9c..214aa1d 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -77,6 +77,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
/**
* A proxy that processes all {@link IInputMethodManager} calls asynchronously.
@@ -175,19 +176,45 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
- offload(() -> mInner.showSoftInput(
- client, windowToken, statsToken, flags, lastClickToolType, resultReceiver, reason));
- return true;
+ @SoftInputShowHideReason int reason, boolean async) {
+
+ if (async) {
+ offload(() -> mInner.showSoftInput(
+ client, windowToken, statsToken, flags, lastClickToolType, resultReceiver,
+ reason, async));
+ return true;
+ } else {
+ final var future = CompletableFuture.supplyAsync(
+ () -> mInner.showSoftInput(
+ client,
+ windowToken,
+ statsToken,
+ flags,
+ lastClickToolType,
+ resultReceiver,
+ reason,
+ async),
+ this::offload);
+ return future.completeOnTimeout(false, 1, TimeUnit.SECONDS).join();
+ }
}
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
- offload(() -> mInner.hideSoftInput(
- client, windowToken, statsToken, flags, resultReceiver, reason));
- return true;
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
+
+ if (async) {
+ offload(() -> mInner.hideSoftInput(
+ client, windowToken, statsToken, flags, resultReceiver, reason, async));
+ return true;
+ } else {
+ final var future = CompletableFuture.supplyAsync(
+ () -> mInner.hideSoftInput(
+ client, windowToken, statsToken, flags, resultReceiver, reason, async),
+ this::offload);
+ return future.completeOnTimeout(false, 1, TimeUnit.SECONDS).join();
+ }
}
@Override
@@ -207,7 +234,8 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
offload(() -> {
InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
windowToken, startInputFlags, softInputMode, windowFlags,
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index a439f16..1740010 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -80,8 +80,8 @@
"ENABLE_PSDS_PERIODIC_DOWNLOAD";
private static final String CONFIG_ENABLE_ACTIVE_SIM_EMERGENCY_SUPL =
"ENABLE_ACTIVE_SIM_EMERGENCY_SUPL";
- private static final String CONFIG_ENABLE_NI_SUPL_MESSAGE_INJECTION =
- "ENABLE_NI_SUPL_MESSAGE_INJECTION";
+ private static final String CONFIG_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL =
+ "ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL";
static final String CONFIG_LONGTERM_PSDS_SERVER_1 = "LONGTERM_PSDS_SERVER_1";
static final String CONFIG_LONGTERM_PSDS_SERVER_2 = "LONGTERM_PSDS_SERVER_2";
static final String CONFIG_LONGTERM_PSDS_SERVER_3 = "LONGTERM_PSDS_SERVER_3";
@@ -230,7 +230,8 @@
* Default false if not set.
*/
boolean isNiSuplMessageInjectionEnabled() {
- return getBooleanConfig(CONFIG_ENABLE_NI_SUPL_MESSAGE_INJECTION, false);
+ return getBooleanConfig(CONFIG_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL,
+ false);
}
/**
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 1938150..4b2c12a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -68,6 +68,7 @@
import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.LocationResult.BadLocationException;
+import android.location.flags.Flags;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -310,6 +311,7 @@
private String mC2KServerHost;
private int mC2KServerPort;
private boolean mSuplEsEnabled = false;
+ private boolean mNiSuplMessageListenerRegistered = false;
private final LocationExtras mLocationExtras = new LocationExtras();
private final NetworkTimeHelper mNetworkTimeHelper;
@@ -387,6 +389,10 @@
// Reload gnss config for no SIM case
mGnssConfiguration.reloadGpsProperties();
}
+ if (Flags.enableNiSuplMessageInjectionByCarrierConfig()) {
+ updateNiSuplMessageListenerRegistration(
+ mGnssConfiguration.isNiSuplMessageInjectionEnabled());
+ }
}
private void reloadGpsProperties() {
@@ -532,28 +538,9 @@
intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
mContext.registerReceiver(mIntentReceiver, intentFilter, null, mHandler);
- if (mNetworkConnectivityHandler.isNativeAgpsRilSupported()
- && mGnssConfiguration.isNiSuplMessageInjectionEnabled()) {
- // Listen to WAP PUSH NI SUPL message.
- // See User Plane Location Protocol Candidate Version 3.0,
- // OMA-TS-ULP-V3_0-20110920-C, Section 8.3 OMA Push.
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
- try {
- intentFilter.addDataType("application/vnd.omaloc-supl-init");
- } catch (IntentFilter.MalformedMimeTypeException e) {
- Log.w(TAG, "Malformed SUPL init mime type");
- }
- mContext.registerReceiver(mIntentReceiver, intentFilter, null, mHandler);
-
- // Listen to MT SMS NI SUPL message.
- // See User Plane Location Protocol Candidate Version 3.0,
- // OMA-TS-ULP-V3_0-20110920-C, Section 8.4 MT SMS.
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
- intentFilter.addDataScheme("sms");
- intentFilter.addDataAuthority("localhost", "7275");
- mContext.registerReceiver(mIntentReceiver, intentFilter, null, mHandler);
+ if (!Flags.enableNiSuplMessageInjectionByCarrierConfig()) {
+ updateNiSuplMessageListenerRegistration(
+ mGnssConfiguration.isNiSuplMessageInjectionEnabled());
}
mNetworkConnectivityHandler.registerNetworkCallbacks();
@@ -592,6 +579,20 @@
case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
subscriptionOrCarrierConfigChanged();
break;
+ }
+ }
+ };
+
+ private BroadcastReceiver mNiSuplIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
+ if (action == null) {
+ return;
+ }
+
+ switch (action) {
case Intents.WAP_PUSH_RECEIVED_ACTION:
case Intents.DATA_SMS_RECEIVED_ACTION:
injectSuplInit(intent);
@@ -1442,6 +1443,46 @@
mGnssMetrics.logSvStatus(gnssStatus);
}
+ private void updateNiSuplMessageListenerRegistration(boolean shouldRegister) {
+ if (!mNetworkConnectivityHandler.isNativeAgpsRilSupported()) {
+ return;
+ }
+ if (mNiSuplMessageListenerRegistered == shouldRegister) {
+ return;
+ }
+
+ // WAP PUSH NI SUPL message intent filter.
+ // See User Plane Location Protocol Candidate Version 3.0,
+ // OMA-TS-ULP-V3_0-20110920-C, Section 8.3 OMA Push.
+ IntentFilter wapPushNiIntentFilter = new IntentFilter();
+ wapPushNiIntentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
+ try {
+ wapPushNiIntentFilter
+ .addDataType("application/vnd.omaloc-supl-init");
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Log.w(TAG, "Malformed SUPL init mime type");
+ }
+
+ // MT SMS NI SUPL message intent filter.
+ // See User Plane Location Protocol Candidate Version 3.0,
+ // OMA-TS-ULP-V3_0-20110920-C, Section 8.4 MT SMS.
+ IntentFilter mtSmsNiIntentFilter = new IntentFilter();
+ mtSmsNiIntentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
+ mtSmsNiIntentFilter.addDataScheme("sms");
+ mtSmsNiIntentFilter.addDataAuthority("localhost", "7275");
+
+ if (shouldRegister) {
+ mContext.registerReceiver(mNiSuplIntentReceiver,
+ wapPushNiIntentFilter, null, mHandler);
+ mContext.registerReceiver(mNiSuplIntentReceiver,
+ mtSmsNiIntentFilter, null, mHandler);
+ mNiSuplMessageListenerRegistered = true;
+ } else {
+ mContext.unregisterReceiver(mNiSuplIntentReceiver);
+ mNiSuplMessageListenerRegistered = false;
+ }
+ }
+
private void restartLocationRequest() {
if (DEBUG) Log.d(TAG, "restartLocationRequest");
setStarted(false);
@@ -1631,6 +1672,10 @@
if (dumpAll) {
mNetworkTimeHelper.dump(pw);
pw.println("mSupportsPsds=" + mSupportsPsds);
+ if (Flags.enableNiSuplMessageInjectionByCarrierConfig()) {
+ pw.println("mNiSuplMessageListenerRegistered="
+ + mNiSuplMessageListenerRegistered);
+ }
pw.println(
"PsdsServerConfigured=" + mGnssConfiguration.isLongTermPsdsServerConfigured());
pw.println("native internal state: ");
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index db4d68b..7d44ba1 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);
@@ -895,8 +889,14 @@
// Hide notification first, as tie profile lock takes time
hideEncryptionNotification(new UserHandle(userId));
- if (isCredentialSharableWithParent(userId)) {
- tieProfileLockIfNecessary(userId, LockscreenCredential.createNone());
+ if (android.app.admin.flags.Flags.fixRaceConditionInTieProfileLock()) {
+ synchronized (mSpManager) {
+ tieProfileLockIfNecessary(userId, LockscreenCredential.createNone());
+ }
+ } else {
+ if (isCredentialSharableWithParent(userId)) {
+ tieProfileLockIfNecessary(userId, LockscreenCredential.createNone());
+ }
}
}
});
@@ -1293,7 +1293,13 @@
mStorage.removeChildProfileLock(userId);
removeKeystoreProfileKey(userId);
} else {
- tieProfileLockIfNecessary(userId, profileUserPassword);
+ if (android.app.admin.flags.Flags.fixRaceConditionInTieProfileLock()) {
+ synchronized (mSpManager) {
+ tieProfileLockIfNecessary(userId, profileUserPassword);
+ }
+ } else {
+ tieProfileLockIfNecessary(userId, profileUserPassword);
+ }
}
} catch (IllegalStateException e) {
setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, old, userId);
@@ -3409,8 +3415,13 @@
// It's OK to dump the credential type since anyone with physical access can just
// observe it from the keyguard directly.
pw.println("Quality: " + getKeyguardStoredQuality(userId));
- pw.println("CredentialType: " + LockPatternUtils.credentialTypeToString(
- getCredentialTypeInternal(userId)));
+ final int credentialType = getCredentialTypeInternal(userId);
+ pw.println("CredentialType: "
+ + LockPatternUtils.credentialTypeToString(credentialType));
+ if (credentialType == CREDENTIAL_TYPE_NONE) {
+ pw.println("IsLockScreenDisabled: "
+ + getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, userId));
+ }
pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabledInternal(userId));
pw.println(TextUtils.formatSimple("Metrics: %s",
getUserPasswordMetrics(userId) != null ? "known" : "unknown"));
diff --git a/services/core/java/com/android/server/logcat/TEST_MAPPING b/services/core/java/com/android/server/logcat/TEST_MAPPING
index 5b07cd9..688dbe9 100644
--- a/services/core/java/com/android/server/logcat/TEST_MAPPING
+++ b/services/core/java/com/android/server/logcat/TEST_MAPPING
@@ -1,15 +1,12 @@
{
"presubmit": [
{
- "name": "FrameworksServicesTests_android_server_logcat_Presubmit"
+ "name": "FrameworksServicesTests_android_server_logcat"
}
],
"postsubmit": [
{
- "name": "FrameworksServicesTests",
- "options": [
- {"include-filter": "com.android.server.logcat"}
- ]
+ "name": "FrameworksServicesTests_android_server_logcat"
}
]
}
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index b8900d7..3d6f9cf 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -27,10 +27,11 @@
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.util.Slog;
@@ -47,6 +48,7 @@
private ShortcutHelper mShortcutHelper;
private RankingConfig mConfig;
private ActivityManager mActivityManager;
+ private PackageManager mPackageManager;
private Context mContext;
boolean mSupportsBubble;
@@ -76,6 +78,11 @@
return null;
}
+ if (mPackageManager == null) {
+ if (DBG) Slog.d(TAG, "missing package manager");
+ return null;
+ }
+
boolean notifCanPresentAsBubble = canPresentAsBubble(record)
&& !mActivityManager.isLowRamDevice()
&& record.isConversation()
@@ -133,6 +140,10 @@
mShortcutHelper = helper;
}
+ public void setPackageManager(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
@VisibleForTesting
public void setActivityManager(ActivityManager manager) {
mActivityManager = manager;
@@ -176,30 +187,25 @@
// TODO: check the shortcut intent / ensure it can show in activity view
return true;
}
- return canLaunchInTaskView(mContext, metadata.getIntent(), pkg);
+ return canLaunchInTaskView(metadata.getIntent().getIntent(), pkg,
+ r.getUser().getIdentifier());
}
/**
- * Whether an intent is properly configured to display in an {@link
- * TaskView} for bubbling.
+ * Whether an intent is properly configured to display in a TaskView for bubbling.
*
- * @param context the context to use.
- * @param pendingIntent the pending intent of the bubble.
- * @param packageName the notification package name for this bubble.
+ * @param intent the intent of the bubble.
+ * @param packageName the notification package name for this bubble.
*/
- // Keep checks in sync with BubbleController#canLaunchInTaskView.
- @VisibleForTesting
- protected boolean canLaunchInTaskView(Context context, PendingIntent pendingIntent,
- String packageName) {
- if (pendingIntent == null) {
+ // Keep checks in sync with BubbleController#isResizableActivity.
+ private boolean canLaunchInTaskView(Intent intent, String packageName, int userId) {
+ if (intent == null) {
Slog.w(TAG, "Unable to create bubble -- no intent");
return false;
}
- Intent intent = pendingIntent.getIntent();
- ActivityInfo info = intent != null
- ? intent.resolveActivityInfo(context.getPackageManager(), 0)
- : null;
+ ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intent, 0, userId);
+ ActivityInfo info = resolveInfo != null ? resolveInfo.activityInfo : null;
if (info == null) {
FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
packageName,
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index c8cb54f..925ba17 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -19,6 +19,10 @@
import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT;
import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
+import static com.android.server.notification.ZenLog.traceApplyDeviceEffect;
+import static com.android.server.notification.ZenLog.traceScheduleApplyDeviceEffect;
+
+import android.app.KeyguardManager;
import android.app.UiModeManager;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
@@ -50,6 +54,7 @@
private final Context mContext;
private final ColorDisplayManager mColorDisplayManager;
+ private final KeyguardManager mKeyguardManager;
private final PowerManager mPowerManager;
private final UiModeManager mUiModeManager;
private final WallpaperManager mWallpaperManager;
@@ -64,6 +69,7 @@
DefaultDeviceEffectsApplier(Context context) {
mContext = context;
mColorDisplayManager = context.getSystemService(ColorDisplayManager.class);
+ mKeyguardManager = context.getSystemService(KeyguardManager.class);
mPowerManager = context.getSystemService(PowerManager.class);
mUiModeManager = context.getSystemService(UiModeManager.class);
WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
@@ -77,6 +83,8 @@
if (mLastAppliedEffects.shouldSuppressAmbientDisplay()
!= effects.shouldSuppressAmbientDisplay()) {
try {
+ traceApplyDeviceEffect("suppressAmbientDisplay",
+ effects.shouldSuppressAmbientDisplay());
mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
effects.shouldSuppressAmbientDisplay());
} catch (Exception e) {
@@ -87,6 +95,8 @@
if (mLastAppliedEffects.shouldDisplayGrayscale() != effects.shouldDisplayGrayscale()) {
if (mColorDisplayManager != null) {
try {
+ traceApplyDeviceEffect("displayGrayscale",
+ effects.shouldDisplayGrayscale());
mColorDisplayManager.setSaturationLevel(
effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
: SATURATION_LEVEL_FULL_COLOR);
@@ -99,6 +109,7 @@
if (mLastAppliedEffects.shouldDimWallpaper() != effects.shouldDimWallpaper()) {
if (mWallpaperManager != null) {
try {
+ traceApplyDeviceEffect("dimWallpaper", effects.shouldDimWallpaper());
mWallpaperManager.setWallpaperDimAmount(
effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
: WALLPAPER_DIM_AMOUNT_NORMAL);
@@ -125,15 +136,18 @@
// Changing the theme can be disruptive for the user (Activities are likely recreated, may
// lose some state). Therefore we only apply the change immediately if the rule was
- // activated manually, or we are initializing, or the screen is currently off/dreaming.
+ // activated manually, or we are initializing, or the screen is currently off/dreaming,
+ // or if the device is locked.
if (origin == ZenModeConfig.ORIGIN_INIT
|| origin == ZenModeConfig.ORIGIN_INIT_USER
|| origin == ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI
|| origin == ZenModeConfig.ORIGIN_USER_IN_APP
- || !mPowerManager.isInteractive()) {
+ || !mPowerManager.isInteractive()
+ || (android.app.Flags.modesUi() && mKeyguardManager.isKeyguardLocked())) {
unregisterScreenOffReceiver();
updateNightModeImmediately(useNightMode);
} else {
+ traceScheduleApplyDeviceEffect("nightMode", useNightMode);
registerScreenOffReceiver();
}
}
@@ -150,6 +164,7 @@
private void updateNightModeImmediately(boolean useNightMode) {
Binder.withCleanCallingIdentity(() -> {
try {
+ traceApplyDeviceEffect("nightMode", useNightMode);
mUiModeManager.setAttentionModeThemeOverlay(
useNightMode ? MODE_ATTENTION_THEME_OVERLAY_NIGHT
: MODE_ATTENTION_THEME_OVERLAY_OFF);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 1fdb57c..03fc60c 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -75,7 +75,9 @@
import com.android.internal.util.function.TriPredicate;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.LocalServices;
import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import org.xmlpull.v1.XmlPullParser;
@@ -101,7 +103,7 @@
* - A remote interface definition (aidl) provided by the service used for communication.
*/
abstract public class ManagedServices {
- protected final String TAG = getClass().getSimpleName();
+ protected final String TAG = getClass().getSimpleName().replace('$', '.');
protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
@@ -134,6 +136,7 @@
private final UserProfiles mUserProfiles;
protected final IPackageManager mPm;
protected final UserManager mUm;
+ private final UserManagerInternal mUserManagerInternal;
private final Config mConfig;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -195,6 +198,7 @@
mConfig = getConfig();
mApprovalLevel = APPROVAL_BY_COMPONENT;
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
}
abstract protected Config getConfig();
@@ -950,7 +954,7 @@
|| isPackageOrComponentAllowed(component.getPackageName(), userId))) {
return false;
}
- return isValidService(component, userId);
+ return componentHasBindPermission(component, userId);
}
private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1090,7 +1094,7 @@
return info;
}
throw new SecurityException("Disallowed call from unknown " + getCaption() + ": "
- + service + " " + service.getClass());
+ + service.asBinder() + " " + service.getClass());
}
public boolean isSameUser(IInterface service, int userId) {
@@ -1302,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);
}
}
@@ -1326,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);
}
@@ -1372,9 +1370,14 @@
@GuardedBy("mMutex")
protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
final IntArray activeUsers,
- SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
- mEnabledServicesForCurrentProfiles.clear();
- mEnabledServicesPackageNames.clear();
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser,
+ boolean isVisibleBackgroundUser) {
+ // When it is a visible background user in Automotive MUMD environment,
+ // don't clear mEnabledServicesForCurrentProfile and mEnabledServicesPackageNames.
+ if (!isVisibleBackgroundUser) {
+ mEnabledServicesForCurrentProfiles.clear();
+ mEnabledServicesPackageNames.clear();
+ }
final int nUserIds = activeUsers.size();
for (int i = 0; i < nUserIds; ++i) {
@@ -1395,7 +1398,12 @@
}
componentsToBind.put(userId, add);
-
+ // When it is a visible background user in Automotive MUMD environment,
+ // skip adding items to mEnabledServicesForCurrentProfile
+ // and mEnabledServicesPackageNames.
+ if (isVisibleBackgroundUser) {
+ continue;
+ }
mEnabledServicesForCurrentProfiles.addAll(userComponents);
for (int j = 0; j < userComponents.size(); j++) {
@@ -1443,7 +1451,10 @@
IntArray userIds = mUserProfiles.getCurrentProfileIds();
boolean rebindAllCurrentUsers = mUserProfiles.isProfileUser(userToRebind, mContext)
&& allowRebindForParentUser();
+ boolean isVisibleBackgroundUser = false;
if (userToRebind != USER_ALL && !rebindAllCurrentUsers) {
+ isVisibleBackgroundUser =
+ mUserManagerInternal.isVisibleBackgroundFullUser(userToRebind);
userIds = new IntArray(1);
userIds.add(userToRebind);
}
@@ -1458,7 +1469,8 @@
// Filter approvedComponentsByUser to collect all of the components that are allowed
// for the currently active user(s).
- populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser);
+ populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser,
+ isVisibleBackgroundUser);
// For every current non-system connection, disconnect services that are no longer
// approved, or ALL services if we are force rebinding
@@ -1573,6 +1585,9 @@
// after the rebind delay
if (isPackageOrComponentAllowedWithPermission(cn, userId)) {
registerService(cn, userId);
+ } else {
+ if (DEBUG) Slog.v(TAG, "skipped reregisterService cn=" + cn + " u=" + userId
+ + " because of isPackageOrComponentAllowedWithPermission check");
}
}
@@ -1906,6 +1921,7 @@
.append(",targetSdkVersion=").append(targetSdkVersion)
.append(",connection=").append(connection == null ? null : "<connection>")
.append(",service=").append(service)
+ .append(",serviceAsBinder=").append(service != null ? service.asBinder() : null)
.append(']').toString();
}
@@ -1944,7 +1960,7 @@
@Override
public void binderDied() {
- if (DEBUG) Slog.d(TAG, "binderDied");
+ if (DEBUG) Slog.d(TAG, "binderDied " + this);
// Remove the service, but don't unbind from the service. The system will bring the
// service back up, and the onServiceConnected handler will read the service with the
// new binding. If this isn't a bound service, and is just a registered
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index dbe778e..e2ec006 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3020,6 +3020,7 @@
BubbleExtractor bubbsExtractor = mRankingHelper.findExtractor(BubbleExtractor.class);
if (bubbsExtractor != null) {
bubbsExtractor.setShortcutHelper(mShortcutHelper);
+ bubbsExtractor.setPackageManager(mPackageManagerClient);
}
registerNotificationPreferencesPullers();
if (mLockUtils == null) {
@@ -7600,16 +7601,14 @@
+ " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
}
- if (android.app.Flags.secureAllowlistToken()) {
- IBinder allowlistToken = notification.getAllowlistToken();
- if (allowlistToken != null && allowlistToken != ALLOWLIST_TOKEN) {
- throw new SecurityException(
- "Unexpected allowlist token received from " + callingUid);
- }
- // allowlistToken is populated by unparceling, so it can be null if the notification was
- // posted from inside system_server. Ensure it's the expected value.
- notification.overrideAllowlistToken(ALLOWLIST_TOKEN);
+ IBinder allowlistToken = notification.getAllowlistToken();
+ if (allowlistToken != null && allowlistToken != ALLOWLIST_TOKEN) {
+ throw new SecurityException(
+ "Unexpected allowlist token received from " + callingUid);
}
+ // allowlistToken is populated by unparceling, so it can be null if the notification was
+ // posted from inside system_server. Ensure it's the expected value.
+ notification.overrideAllowlistToken(ALLOWLIST_TOKEN);
checkRestrictedCategories(notification);
@@ -8774,12 +8773,10 @@
*/
private boolean enqueueNotification() {
synchronized (mNotificationLock) {
- if (android.app.Flags.secureAllowlistToken()) {
- // allowlistToken is populated by unparceling, so it will be absent if the
- // EnqueueNotificationRunnable is created directly by NMS (as we do for group
- // summaries) instead of via notify(). Fix that.
- r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
- }
+ // allowlistToken is populated by unparceling, so it will be absent if the
+ // EnqueueNotificationRunnable is created directly by NMS (as we do for group
+ // summaries) instead of via notify(). Fix that.
+ r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
final long snoozeAt =
mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index e541246..b9f0968 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -330,10 +330,19 @@
}
final long[] vibrationPattern = channel.getVibrationPattern();
- if (vibrationPattern == null) {
- return helper.createDefaultVibration(insistent);
+ if (vibrationPattern != null) {
+ return helper.createWaveformVibration(vibrationPattern, insistent);
}
- return helper.createWaveformVibration(vibrationPattern, insistent);
+
+ if (com.android.server.notification.Flags.notificationVibrationInSoundUri()) {
+ final VibrationEffect vibrationEffectFromSoundUri =
+ helper.createVibrationEffectFromSoundUri(channel.getSound());
+ if (vibrationEffectFromSoundUri != null) {
+ return vibrationEffectFromSoundUri;
+ }
+ }
+
+ return helper.createDefaultVibration(insistent);
}
private VibrationEffect calculateVibration() {
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index 8a0e595..fbe7772 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -24,6 +24,9 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.media.AudioAttributes;
+import android.media.RingtoneManager;
+import android.media.Utils;
+import android.net.Uri;
import android.os.Process;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -51,6 +54,7 @@
@Nullable private final float[] mDefaultPwlePattern;
@Nullable private final float[] mFallbackPwlePattern;
private final int mDefaultVibrationAmplitude;
+ private final Context mContext;
public VibratorHelper(Context context) {
mVibrator = context.getSystemService(Vibrator.class);
@@ -68,6 +72,7 @@
com.android.internal.R.array.config_notificationFallbackVibeWaveform);
mDefaultVibrationAmplitude = context.getResources().getInteger(
com.android.internal.R.integer.config_defaultVibrationAmplitude);
+ mContext = context;
}
/**
@@ -184,6 +189,16 @@
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
public VibrationEffect createDefaultVibration(boolean insistent) {
+ if (com.android.server.notification.Flags.notificationVibrationInSoundUri()) {
+ final Uri defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(mContext,
+ RingtoneManager.TYPE_NOTIFICATION);
+ final VibrationEffect vibrationEffectFromSoundUri =
+ createVibrationEffectFromSoundUri(defaultRingtoneUri);
+ if (vibrationEffectFromSoundUri != null) {
+ return vibrationEffectFromSoundUri;
+ }
+ }
+
if (mVibrator.hasFrequencyControl()) {
VibrationEffect effect = createPwleWaveformVibration(mDefaultPwlePattern, insistent);
if (effect != null) {
@@ -193,6 +208,22 @@
return createWaveformVibration(mDefaultPattern, insistent);
}
+ /**
+ * Safely create a {@link VibrationEffect} from given an uri {@code Uri}.
+ * with query parameter "vibration_uri"
+ *
+ * Use this function if the {@code Uri} is with a query parameter "vibration_uri" and the
+ * vibration_uri represents a valid vibration effect in xml
+ *
+ * @param uri {@code Uri} an uri including query parameter "vibraiton_uri"
+ */
+ public @Nullable VibrationEffect createVibrationEffectFromSoundUri(Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ return Utils.parseVibrationEffect(mVibrator, Utils.getVibrationUri(uri));
+ }
+
/** Returns if a given vibration can be played by the vibrator that does notification buzz. */
public boolean areEffectComponentsSupported(VibrationEffect effect) {
return mVibrator.areVibrationFeaturesSupported(effect);
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 82c5733..1aa5ac0 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -23,18 +23,15 @@
import android.os.Build;
import android.os.RemoteException;
import android.provider.Settings.Global;
-import android.service.notification.Condition;
import android.service.notification.IConditionProvider;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeDiff;
import android.util.LocalLog;
-import android.util.Log;
-import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.Date;
import java.util.List;
public class ZenLog {
@@ -61,6 +58,8 @@
private static final int TYPE_RECORD_CALLER = 19;
private static final int TYPE_CHECK_REPEAT_CALLER = 20;
private static final int TYPE_ALERT_ON_UPDATED_INTERCEPT = 21;
+ private static final int TYPE_APPLY_DEVICE_EFFECT = 22;
+ private static final int TYPE_SCHEDULE_APPLY_DEVICE_EFFECT = 23;
public static void traceIntercepted(NotificationRecord record, String reason) {
append(TYPE_INTERCEPTED, record.getKey() + "," + reason);
@@ -173,6 +172,14 @@
+ ", given uri=" + hasUri);
}
+ public static void traceApplyDeviceEffect(String effect, boolean newValue) {
+ append(TYPE_APPLY_DEVICE_EFFECT, effect + " -> " + newValue);
+ }
+
+ public static void traceScheduleApplyDeviceEffect(String effect, boolean scheduledValue) {
+ append(TYPE_SCHEDULE_APPLY_DEVICE_EFFECT, effect + " -> " + scheduledValue);
+ }
+
private static String subscribeResult(IConditionProvider provider, RemoteException e) {
return provider == null ? "no provider" : e != null ? e.getMessage() : "ok";
}
@@ -196,6 +203,8 @@
case TYPE_RECORD_CALLER: return "record_caller";
case TYPE_CHECK_REPEAT_CALLER: return "check_repeat_caller";
case TYPE_ALERT_ON_UPDATED_INTERCEPT: return "alert_on_updated_intercept";
+ case TYPE_APPLY_DEVICE_EFFECT: return "apply_device_effect";
+ case TYPE_SCHEDULE_APPLY_DEVICE_EFFECT: return "schedule_device_effect";
default: return "unknown";
}
}
@@ -273,4 +282,14 @@
STATE_CHANGES.dump(prefix, pw);
}
}
+
+ @VisibleForTesting(/* otherwise = VisibleForTesting.NONE */)
+ public static void clear() {
+ synchronized (INTERCEPTION_EVENTS) {
+ INTERCEPTION_EVENTS.clear();
+ }
+ synchronized (STATE_CHANGES) {
+ STATE_CHANGES.clear();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 6ff0e04..2ada9ae4 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -226,10 +226,8 @@
mDefaultConfig = Flags.modesUi()
? ZenModeConfig.getDefaultConfig()
: readDefaultConfig(mContext.getResources());
- updateDefaultConfigAutomaticRules();
- if (Flags.modesApi()) {
- updateDefaultAutomaticRulePolicies();
- }
+ updateDefaultConfig(mContext, mDefaultConfig);
+
mConfig = mDefaultConfig.copy();
synchronized (mConfigsArrayLock) {
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
@@ -1073,7 +1071,7 @@
}
void updateZenRulesOnLocaleChange() {
- updateDefaultConfigAutomaticRules();
+ updateRuleStringsForCurrentLocale(mContext, mDefaultConfig);
synchronized (mConfigLock) {
if (mConfig == null) {
return;
@@ -2127,17 +2125,25 @@
@GuardedBy("mConfigLock")
private void applyCustomPolicy(ZenPolicy policy, ZenRule rule, boolean useManualConfig) {
if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
- policy.apply(new ZenPolicy.Builder()
- .disallowAllSounds()
- .allowPriorityChannels(false)
- .build());
+ if (Flags.modesApi() && Flags.modesUi()) {
+ policy.apply(ZenPolicy.getBasePolicyInterruptionFilterNone());
+ } else {
+ policy.apply(new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowPriorityChannels(false)
+ .build());
+ }
} else if (rule.zenMode == Global.ZEN_MODE_ALARMS) {
- policy.apply(new ZenPolicy.Builder()
- .disallowAllSounds()
- .allowAlarms(true)
- .allowMedia(true)
- .allowPriorityChannels(false)
- .build());
+ if (Flags.modesApi() && Flags.modesUi()) {
+ policy.apply(ZenPolicy.getBasePolicyInterruptionFilterAlarms());
+ } else {
+ policy.apply(new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowPriorityChannels(false)
+ .build());
+ }
} else if (rule.zenPolicy != null) {
policy.apply(rule.zenPolicy);
} else {
@@ -2229,30 +2235,49 @@
}
}
- private void updateDefaultConfigAutomaticRules() {
- for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+ /**
+ * Apply changes to the <em>default</em> {@link ZenModeConfig} so that the rules included by
+ * default (Events / Sleeping) support the latest Zen features and are ready for new users.
+ *
+ * <p>This includes: setting a fully populated ZenPolicy, setting correct type and
+ * allowManualInvocation=true, and ensuring default names and trigger descriptions correspond
+ * to the current locale.
+ */
+ private static void updateDefaultConfig(Context context, ZenModeConfig defaultConfig) {
+ if (Flags.modesApi()) {
+ updateDefaultAutomaticRulePolicies(defaultConfig);
+ }
+ if (Flags.modesApi() && Flags.modesUi()) {
+ SystemZenRules.maybeUpgradeRules(context, defaultConfig);
+ }
+ updateRuleStringsForCurrentLocale(context, defaultConfig);
+ }
+
+ private static void updateRuleStringsForCurrentLocale(Context context,
+ ZenModeConfig defaultConfig) {
+ for (ZenRule rule : defaultConfig.automaticRules.values()) {
if (ZenModeConfig.EVENTS_DEFAULT_RULE_ID.equals(rule.id)) {
- rule.name = mContext.getResources()
+ rule.name = context.getResources()
.getString(R.string.zen_mode_default_events_name);
} else if (ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID.equals(rule.id)) {
- rule.name = mContext.getResources()
+ rule.name = context.getResources()
.getString(R.string.zen_mode_default_every_night_name);
}
if (Flags.modesApi() && Flags.modesUi()) {
- SystemZenRules.updateTriggerDescription(mContext, rule);
+ SystemZenRules.updateTriggerDescription(context, rule);
}
}
}
// Updates the policies in the default automatic rules (provided via default XML config) to
// be fully filled in default values.
- private void updateDefaultAutomaticRulePolicies() {
+ private static void updateDefaultAutomaticRulePolicies(ZenModeConfig defaultConfig) {
if (!Flags.modesApi()) {
// Should be checked before calling, but just in case.
return;
}
- ZenPolicy defaultPolicy = mDefaultConfig.getZenPolicy();
- for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+ ZenPolicy defaultPolicy = defaultConfig.getZenPolicy();
+ for (ZenRule rule : defaultConfig.automaticRules.values()) {
if (ZenModeConfig.DEFAULT_RULE_IDS.contains(rule.id) && rule.zenPolicy == null) {
rule.zenPolicy = defaultPolicy.copy();
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index aac2c40..be3adc1 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -156,3 +156,10 @@
description: "This flag enables forced auto-grouping conversations"
bug: "336488844"
}
+
+flag {
+ name: "notification_vibration_in_sound_uri"
+ namespace: "systemui"
+ description: "This flag enables sound uri with vibration source"
+ bug: "358524009"
+}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 6303ecd..a41675a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -298,12 +298,13 @@
restoreSettings();
- // Wipe all shell overlays on boot, to recover from a potentially broken device
- String shellPkgName = TextUtils.emptyIfNull(
- getContext().getString(android.R.string.config_systemShell));
- mSettings.removeIf(overlayInfo -> overlayInfo.isFabricated
- && shellPkgName.equals(overlayInfo.packageName));
-
+ if (Build.IS_USER) {
+ // Wipe all shell overlays on boot, to recover from a potentially broken device
+ String shellPkgName = TextUtils.emptyIfNull(
+ getContext().getString(android.R.string.config_systemShell));
+ mSettings.removeIf(overlayInfo -> overlayInfo.isFabricated
+ && shellPkgName.equals(overlayInfo.packageName));
+ }
initIfNeeded();
onStartUser(UserHandle.USER_SYSTEM);
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 1317866..f449126 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;
@@ -209,6 +207,7 @@
import java.util.Set;
import java.util.concurrent.ExecutorService;
+
final class InstallPackageHelper {
private final PackageManagerService mPm;
private final AppDataHelper mAppDataHelper;
@@ -989,15 +988,6 @@
return false;
}
- void installPackagesTraced(List<InstallRequest> requests) {
- try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
- installPackagesLI(requests);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
/**
* Installs one or more packages atomically. This operation is broken up into four phases:
* <ul>
@@ -1017,15 +1007,31 @@
*
* Failure at any phase will result in a full failure to install all packages.
*/
- @GuardedBy("mPm.mInstallLock")
- private void installPackagesLI(List<InstallRequest> requests) {
- final Set<String> scannedPackages = new ArraySet<>(requests.size());
- final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
- final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
- CriticalEventLog.getInstance().logInstallPackagesStarted();
+ void installPackagesTraced(List<InstallRequest> requests) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
boolean success = false;
+ final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
+ final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
+ CriticalEventLog.getInstance().logInstallPackagesStarted();
+
+ if (prepareInstallPackages(requests)
+ && scanInstallPackages(requests, createdAppId, versionInfos)) {
+ List<ReconciledPackage> reconciledPackages =
+ reconcileInstallPackages(requests, versionInfos);
+ if (reconciledPackages != null && commitInstallPackages(reconciledPackages)) {
+ success = true;
+ }
+ }
+ } finally {
+ completeInstallProcess(requests, createdAppId, success);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private boolean prepareInstallPackages(List<InstallRequest> requests) {
+ // TODO: will remove the locking after doRename is moved out of prepare
+ try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
for (InstallRequest request : requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
@@ -1036,17 +1042,27 @@
prepareFailure.getMessage());
request.setOriginPackage(prepareFailure.mConflictingPackage);
request.setOriginPermission(prepareFailure.mConflictingPermission);
- return;
+ return false;
} finally {
request.onPrepareFinished();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+ }
+ }
+ return true;
+ }
+ private boolean scanInstallPackages(List<InstallRequest> requests,
+ Map<String, Boolean> createdAppId, Map<String, Settings.VersionInfo> versionInfos) {
+ // TODO(b/362840929): remove locker
+ try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
+ final Set<String> scannedPackages = new ArraySet<>(requests.size());
+ for (InstallRequest request : requests) {
final ParsedPackage packageToScan = request.getParsedPackage();
if (packageToScan == null) {
request.setError(INSTALL_FAILED_SESSION_INVALID,
"Failed to obtain package to scan");
- return;
+ return false;
}
request.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
final String packageName = packageToScan.getPackageName();
@@ -1064,7 +1080,7 @@
"Duplicate package "
+ packageName
+ " in multi-package install request.");
- return;
+ return false;
}
if (!checkNoAppStorageIsConsistent(
request.getScanRequestOldPackage(), packageToScan)) {
@@ -1074,7 +1090,7 @@
INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Update attempted to change value of "
+ PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
- return;
+ return false;
}
final boolean isApex = (request.getScanFlags() & SCAN_AS_APEX) != 0;
final boolean isSdkLibrary = packageToScan.isSdkLibrary();
@@ -1087,7 +1103,7 @@
mPm.getSettingsVersionForPackage(packageToScan));
} catch (PackageManagerException e) {
request.setError("Scanning Failed.", e);
- return;
+ return false;
}
if (request.isArchived()) {
final SparseArray<String> responsibleInstallerTitles =
@@ -1099,17 +1115,22 @@
request.setError(PackageManagerException.ofInternalError(
"Failed to obtain the responsible installer info",
INTERNAL_ERROR_ARCHIVE_NO_INSTALLER_TITLE));
- return;
+ return false;
}
request.setResponsibleInstallerTitles(responsibleInstallerTitles);
}
}
+ }
+ return true;
+ }
- List<ReconciledPackage> reconciledPackages;
+ private List<ReconciledPackage> reconcileInstallPackages(List<InstallRequest> requests,
+ Map<String, Settings.VersionInfo> versionInfos) {
+ try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
synchronized (mPm.mLock) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
- reconciledPackages = ReconcilePackageUtils.reconcilePackages(
+ return ReconcilePackageUtils.reconcilePackages(
requests, Collections.unmodifiableMap(mPm.mPackages),
versionInfos, mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
mPm.mSettings, mPm.mInjector.getSystemConfig());
@@ -1117,70 +1138,80 @@
for (InstallRequest request : requests) {
request.setError("Reconciliation failed...", e);
}
- return;
+ return null;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- if (Flags.improveInstallFreeze()) {
- // Postpone freezer until after reconcile
- for (ReconciledPackage reconciledPkg : reconciledPackages) {
- InstallRequest installRequest = reconciledPkg.mInstallRequest;
- String packageName = installRequest.getParsedPackage().getPackageName();
- PackageFreezer freezer = freezePackageForInstall(packageName,
- UserHandle.USER_ALL, installRequest.getInstallFlags(),
- "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED,
- installRequest);
- installRequest.setFreezer(freezer);
- }
+ }
+ }
+ }
+
+
+ private boolean commitInstallPackages(List<ReconciledPackage> reconciledPackages) {
+ try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
+ if (Flags.improveInstallFreeze()) {
+ // Postpone freezer until after reconcile
+ for (ReconciledPackage reconciledPkg : reconciledPackages) {
+ InstallRequest installRequest = reconciledPkg.mInstallRequest;
+ String packageName = installRequest.getParsedPackage().getPackageName();
+ PackageFreezer freezer = freezePackageForInstall(packageName,
+ UserHandle.USER_ALL, installRequest.getInstallFlags(),
+ "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED,
+ installRequest);
+ installRequest.setFreezer(freezer);
}
+ }
+ synchronized (mPm.mLock) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());
- success = true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
executePostCommitStepsLIF(reconciledPackages);
- } finally {
- if (success) {
- for (InstallRequest request : requests) {
- if (request.getDataLoaderType() != DataLoaderType.INCREMENTAL) {
- continue;
- }
- if (request.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
- continue;
- }
- // For incremental installs, we bypass the verifier prior to install. Now
- // that we know the package is valid, send a notice to the verifier with
- // the root hash of the base.apk.
- final String baseCodePath = request.getPkg().getBaseApkPath();
- final String[] splitCodePaths = request.getPkg().getSplitCodePaths();
- final Uri originUri = request.getOriginUri();
- final int verificationId = mPm.mPendingVerificationToken++;
- final String rootHashString = PackageManagerServiceUtils
- .buildVerificationRootHashString(baseCodePath, splitCodePaths);
- VerificationUtils.broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_ALLOW, rootHashString,
- request.getDataLoaderType(), request.getUser(), mContext);
+ }
+ return true;
+ }
+
+ private void completeInstallProcess(List<InstallRequest> requests,
+ Map<String, Boolean> createdAppId, boolean success) {
+ if (success) {
+ for (InstallRequest request : requests) {
+ if (request.getDataLoaderType() != DataLoaderType.INCREMENTAL) {
+ continue;
}
- } else {
- for (InstallRequest installRequest : requests) {
- if (installRequest.getParsedPackage() != null && createdAppId.getOrDefault(
- installRequest.getParsedPackage().getPackageName(), false)) {
- cleanUpAppIdCreation(installRequest);
- }
+ if (request.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
+ continue;
}
- // TODO(b/194319951): create a more descriptive reason than unknown
- // mark all non-failure installs as UNKNOWN so we do not treat them as success
- for (InstallRequest request : requests) {
- request.closeFreezer();
- if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
- request.setReturnCode(PackageManager.INSTALL_UNKNOWN);
- }
+ // For incremental installs, we bypass the verifier prior to install. Now
+ // that we know the package is valid, send a notice to the verifier with
+ // the root hash of the base.apk.
+ final String baseCodePath = request.getPkg().getBaseApkPath();
+ final String[] splitCodePaths = request.getPkg().getSplitCodePaths();
+ final Uri originUri = request.getOriginUri();
+ final int verificationId = mPm.mPendingVerificationToken++;
+ final String rootHashString = PackageManagerServiceUtils
+ .buildVerificationRootHashString(baseCodePath, splitCodePaths);
+ VerificationUtils.broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_ALLOW, rootHashString,
+ request.getDataLoaderType(), request.getUser(), mContext);
+ }
+ } else {
+ for (InstallRequest installRequest : requests) {
+ if (installRequest.getParsedPackage() != null && createdAppId.getOrDefault(
+ installRequest.getParsedPackage().getPackageName(), false)) {
+ cleanUpAppIdCreation(installRequest);
}
}
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ // TODO(b/194319951): create a more descriptive reason than unknown
+ // mark all non-failure installs as UNKNOWN so we do not treat them as success
+ for (InstallRequest request : requests) {
+ request.closeFreezer();
+ if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
+ request.setReturnCode(PackageManager.INSTALL_UNKNOWN);
+ }
+ }
}
}
@@ -1851,10 +1882,16 @@
}
if (!oldSharedUid.equals(newSharedUid)) {
- throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
- "Package " + parsedPackage.getPackageName()
- + " shared user changed from "
- + oldSharedUid + " to " + newSharedUid);
+ if (!(oldSharedUid.equals("<nothing>") && ps.getPkg() == null
+ && ps.isArchivedOnAnyUser(allUsers))) {
+ // Only allow changing sharedUserId if unarchiving
+ // TODO(b/361558423): remove this check after pre-archiving installs
+ // accept a sharedUserId param in the API
+ throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
+ "Package " + parsedPackage.getPackageName()
+ + " shared user changed from "
+ + oldSharedUid + " to " + newSharedUid);
+ }
}
// APK should not re-join shared UID
@@ -2600,70 +2637,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/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 5e45b4c..76ea0b9 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -283,10 +283,7 @@
return START_CLASS_NOT_FOUND;
}
- String currentLauncherPackageName = getCurrentLauncherPackageName(getParentUserId(userId));
- if ((currentLauncherPackageName == null || !TextUtils.equals(callerPackageName,
- currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
- // TODO(b/311619990): Remove dependency on SHELL_UID for testing
+ if (!isCallerQualifiedForUnarchival(callerPackageName, callingUid, userId)) {
Slog.e(TAG, TextUtils.formatSimple(
"callerPackageName: %s does not qualify for unarchival of package: " + "%s!",
callerPackageName, packageName));
@@ -335,6 +332,37 @@
return START_ABORTED;
}
+ private boolean isCallerQualifiedForUnarchival(String callerPackageName, int callingUid,
+ int userId) {
+ // TODO(b/311619990): Remove dependency on SHELL_UID for testing
+ if (callingUid == Process.SHELL_UID) {
+ return true;
+ }
+ String currentLauncherPackageName = getCurrentLauncherPackageName(getParentUserId(userId));
+ if (currentLauncherPackageName != null && TextUtils.equals(
+ callerPackageName, currentLauncherPackageName)) {
+ return true;
+ }
+ Slog.w(TAG, TextUtils.formatSimple(
+ "Requester of unarchival: %s is not the default launcher package: %s.",
+ callerPackageName, currentLauncherPackageName));
+ // When the default launcher is not set, or when the current caller is not the default
+ // launcher, allow the caller to directly request unarchive if it is a launcher app
+ // that is a pre-installed system app.
+ final Computer snapshot = mPm.snapshotComputer();
+ final PackageStateInternal ps = snapshot.getPackageStateInternal(callerPackageName);
+ final boolean isSystem = ps != null && ps.isSystem();
+ return isSystem && isLauncherApp(snapshot, callerPackageName, userId);
+ }
+
+ private boolean isLauncherApp(Computer snapshot, String packageName, int userId) {
+ final Intent intent = snapshot.getHomeIntent();
+ intent.setPackage(packageName);
+ List<ResolveInfo> launcherActivities = snapshot.queryIntentActivitiesInternal(
+ intent, null /* resolvedType */, 0 /* flags */, userId);
+ return !launcherActivities.isEmpty();
+ }
+
// Profiles share their UI and default apps, so we have to get the profile parent before
// fetching the default launcher.
private int getParentUserId(int userId) {
@@ -936,13 +964,9 @@
* <p> In the rare case the app had multiple launcher activities, only one of the icons is
* returned arbitrarily.
*
- * <p> By default, the icon will be overlay'd with a cloud icon on top. A launcher app can
+ * <p> By default, the icon will be overlay'd with a cloud icon on top. An app can
* disable the cloud overlay via the
* {@link LauncherApps.ArchiveCompatibilityParams#setEnableIconOverlay(boolean)} API.
- * The default launcher's cloud overlay mode determines the cloud overlay status returned by
- * any other callers. That is, if the current launcher has the cloud overlay disabled, any other
- * app that fetches the app icon will also get an icon that has the cloud overlay disabled.
- * This is to prevent style mismatch caused by icons that are fetched by different callers.
*/
@Nullable
public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2124ff6..ff9c3e5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -60,6 +60,7 @@
import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
@@ -3372,8 +3373,10 @@
// TODO(b/261957226): centralise this logic in DPM
boolean isPackageDeviceAdmin(String packageName, int userId) {
final IDevicePolicyManager dpm = getDevicePolicyManager();
+ final DevicePolicyManagerInternal dpmi =
+ mInjector.getLocalService(DevicePolicyManagerInternal.class);
try {
- if (dpm != null) {
+ if (dpm != null && dpmi != null) {
final ComponentName deviceOwnerComponentName = dpm.getDeviceOwnerComponent(
/* callingUserOnly =*/ false);
final String deviceOwnerPackageName = deviceOwnerComponentName == null ? null
@@ -3396,7 +3399,8 @@
if (dpm.packageHasActiveAdmins(packageName, users[i])) {
return true;
}
- if (isDeviceManagementRoleHolder(packageName, users[i])) {
+ if (isDeviceManagementRoleHolder(packageName, users[i])
+ && dpmi.isUserOrganizationManaged(users[i])) {
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index d374142..9428de7 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -42,6 +42,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -924,6 +925,18 @@
return PackageArchiver.isArchived(readUserState(userId));
}
+ /**
+ * @return if the package is archived in any of the users
+ */
+ boolean isArchivedOnAnyUser(int[] userIds) {
+ for (int user : userIds) {
+ if (isArchived(user)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
int getInstallReason(int userId) {
return readUserState(userId).getInstallReason();
}
@@ -981,39 +994,23 @@
}
int[] queryInstalledUsers(int[] users, boolean installed) {
- int num = 0;
+ IntArray installedUsers = new IntArray(users.length);
for (int user : users) {
if (getInstalled(user) == installed) {
- num++;
+ installedUsers.add(user);
}
}
- int[] res = new int[num];
- num = 0;
- for (int user : users) {
- if (getInstalled(user) == installed) {
- res[num] = user;
- num++;
- }
- }
- return res;
+ return installedUsers.toArray();
}
int[] queryUsersInstalledOrHasData(int[] users) {
- int num = 0;
+ IntArray usersInstalledOrHasData = new IntArray(users.length);
for (int user : users) {
if (getInstalled(user) || readUserState(user).dataExists()) {
- num++;
+ usersInstalledOrHasData.add(user);
}
}
- int[] res = new int[num];
- num = 0;
- for (int user : users) {
- if (getInstalled(user) || readUserState(user).dataExists()) {
- res[num] = user;
- num++;
- }
- }
- return res;
+ return usersInstalledOrHasData.toArray();
}
long getCeDataInode(int userId) {
@@ -1283,25 +1280,14 @@
}
public int[] getNotInstalledUserIds() {
- int count = 0;
int userStateCount = mUserStates.size();
+ IntArray notInstalledUsers = new IntArray(userStateCount);
for (int i = 0; i < userStateCount; i++) {
if (!mUserStates.valueAt(i).isInstalled()) {
- count++;
+ notInstalledUsers.add(mUserStates.keyAt(i));
}
}
- if (count == 0) {
- return EmptyArray.INT;
- }
-
- int[] excludedUserIds = new int[count];
- int idx = 0;
- for (int i = 0; i < userStateCount; i++) {
- if (!mUserStates.valueAt(i).isInstalled()) {
- excludedUserIds[idx++] = mUserStates.keyAt(i);
- }
- }
- return excludedUserIds;
+ return notInstalledUsers.toArray();
}
/**
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 26da84f..f9a8968 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -292,8 +292,10 @@
// Step 2: destroy app data.
mAppDataHelper.destroyAppDataLIF(resolvedPkg, userId, appDataDeletionFlags);
if (userId != UserHandle.USER_ALL) {
- ps.setCeDataInode(-1, userId);
- ps.setDeDataInode(-1, userId);
+ synchronized (mPm.mLock) {
+ ps.setCeDataInode(-1, userId);
+ ps.setDeDataInode(-1, userId);
+ }
}
final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm,
@@ -425,19 +427,21 @@
}
final boolean isArchive = (flags & PackageManager.DELETE_ARCHIVE) != 0;
final long currentTimeMillis = System.currentTimeMillis();
- for (int userId : outInfo.mRemovedUsers) {
- if (DEBUG_REMOVE) {
- final boolean wasInstalled = deletedPs.getInstalled(userId);
- Slog.d(TAG, " user " + userId + ": " + wasInstalled + " => " + false);
+ synchronized (mPm.mLock) {
+ for (int userId : outInfo.mRemovedUsers) {
+ if (DEBUG_REMOVE) {
+ final boolean wasInstalled = deletedPs.getInstalled(userId);
+ Slog.d(TAG, " user " + userId + ": " + wasInstalled + " => " + false);
+ }
+ deletedPs.setInstalled(/* installed= */ false, userId);
}
- deletedPs.setInstalled(/* installed= */ false, userId);
- }
- // Preserve split apk information for downgrade check with DELETE_KEEP_DATA and archived
- // app cases
- if (deletedPkg != null && deletedPkg.getSplitNames() != null) {
- deletedPs.setSplitNames(deletedPkg.getSplitNames());
- deletedPs.setSplitRevisionCodes(deletedPkg.getSplitRevisionCodes());
+ // Preserve split apk information for downgrade check with DELETE_KEEP_DATA and
+ // archived app cases
+ if (deletedPkg != null && deletedPkg.getSplitNames() != null) {
+ deletedPs.setSplitNames(deletedPkg.getSplitNames());
+ deletedPs.setSplitRevisionCodes(deletedPkg.getSplitRevisionCodes());
+ }
}
}
@@ -448,17 +452,19 @@
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
- for (int userId : allUserHandles) {
- final boolean installed = ArrayUtils.contains(outInfo.mOrigUsers, userId);
- if (DEBUG_REMOVE) {
- Slog.d(TAG, " user " + userId + " => " + installed);
- }
- if (installed != deletedPs.getInstalled(userId)) {
- installedStateChanged = true;
- }
- deletedPs.setInstalled(installed, userId);
- if (installed) {
- deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
+ synchronized (mPm.mLock) {
+ for (int userId : allUserHandles) {
+ final boolean installed = ArrayUtils.contains(outInfo.mOrigUsers, userId);
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, " user " + userId + " => " + installed);
+ }
+ if (installed != deletedPs.getInstalled(userId)) {
+ installedStateChanged = true;
+ }
+ deletedPs.setInstalled(installed, userId);
+ if (installed) {
+ deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4c9be21..1f672a0 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5575,18 +5575,18 @@
}
if (!paths.getOverlayPaths().isEmpty()) {
pw.print(prefix);
- pw.println(" ");
+ pw.print(" ");
pw.print(libOverlayPaths.getKey());
pw.println(" overlay paths:");
for (String path : paths.getOverlayPaths()) {
pw.print(prefix);
- pw.print(" ");
+ pw.print(" ");
pw.println(path);
}
}
if (!paths.getResourceDirs().isEmpty()) {
pw.print(prefix);
- pw.println(" ");
+ pw.print(" ");
pw.print(libOverlayPaths.getKey());
pw.println(" legacy overlay paths:");
for (String path : paths.getResourceDirs()) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 25468fa..a3ff195 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -588,7 +588,7 @@
void handleOnDefaultLauncherChanged(int userId) {
if (DEBUG) {
- Slog.v(TAG, "Default launcher changed for user: " + userId);
+ Slog.v(TAG, "Default launcher changed for userId=" + userId);
}
// Default launcher is removed or changed, revoke all URI permissions.
@@ -712,7 +712,7 @@
/** lifecycle event */
void handleUnlockUser(int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "handleUnlockUser: user=" + userId);
+ Slog.d(TAG, "handleUnlockUser: userId=" + userId);
}
synchronized (mUnlockedUsers) {
mUnlockedUsers.put(userId, true);
@@ -739,7 +739,7 @@
/** lifecycle event */
void handleStopUser(int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "handleStopUser: user=" + userId);
+ Slog.d(TAG, "handleStopUser: userId=" + userId);
}
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleStopUser");
synchronized (mServiceLock) {
@@ -755,7 +755,7 @@
@GuardedBy("mServiceLock")
private void unloadUserLocked(int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "unloadUserLocked: user=" + userId);
+ Slog.d(TAG, "unloadUserLocked: userId=" + userId);
}
// Cancel any ongoing background tasks.
getUserShortcutsLocked(userId).cancelAllInFlightTasks();
@@ -1221,7 +1221,7 @@
private void scheduleSaveInner(@UserIdInt int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "Scheduling to save for " + userId);
+ Slog.d(TAG, "Scheduling to save for userId=" + userId);
}
synchronized (mServiceLock) {
if (!mDirtyUserIds.contains(userId)) {
@@ -1333,7 +1333,7 @@
// Requires mServiceLock held, but "Locked" prefix would look weird so we just say "L".
void throwIfUserLockedL(@UserIdInt int userId) {
if (!isUserUnlockedL(userId)) {
- throw new IllegalStateException("User " + userId + " is locked or not running");
+ throw new IllegalStateException("User (with userId=" + userId + ") is locked or not running");
}
}
@@ -1720,7 +1720,7 @@
// Otherwise, make sure the arguments are valid.
if (UserHandle.getUserId(callingUid) != userId) {
- throw new SecurityException("Invalid user-ID");
+ throw new SecurityException("Invalid userId");
}
}
@@ -1735,7 +1735,7 @@
// Otherwise, make sure the arguments are valid.
if (UserHandle.getUserId(callingUid) != userId) {
- throw new SecurityException("Invalid user-ID");
+ throw new SecurityException("Invalid userId");
}
if (injectGetPackageUid(packageName, userId) != callingUid) {
throw new SecurityException("Calling package name mismatch");
@@ -1755,7 +1755,7 @@
}
final int callingUid = injectBinderCallingUid();
if (UserHandle.getUserId(callingUid) != si.getUserId()) {
- throw new SecurityException("User-ID in shortcut doesn't match the caller");
+ throw new SecurityException("UserId in shortcut doesn't match the caller");
}
}
@@ -1822,7 +1822,7 @@
final int userId = sp.getPackageUserId();
if (DEBUG) {
Slog.d(TAG, String.format(
- "Shortcut changes: package=%s, user=%d", packageName, userId));
+ "Shortcut changes: package=%s, userId=%d", packageName, userId));
}
injectPostToHandlerDebounced(sp, notifyListenerRunnable(packageName, userId));
notifyShortcutChangeCallbacks(packageName, userId, changedShortcuts, removedShortcuts);
@@ -1832,7 +1832,7 @@
private void notifyListeners(@NonNull final String packageName, @UserIdInt final int userId) {
if (DEBUG) {
Slog.d(TAG, String.format(
- "Shortcut changes: package=%s, user=%d", packageName, userId));
+ "Shortcut changes: package=%s, userId=%d", packageName, userId));
}
injectPostToHandler(notifyListenerRunnable(packageName, userId));
}
@@ -2736,14 +2736,14 @@
void resetThrottlingInner(@UserIdInt int userId) {
synchronized (mServiceLock) {
if (!isUserUnlockedL(userId)) {
- Log.w(TAG, "User " + userId + " is locked or not running");
+ Log.w(TAG, "User (with userId=" + userId + ") is locked or not running");
return;
}
getUserShortcutsLocked(userId).resetThrottling();
}
scheduleSaveUser(userId);
- Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
+ Slog.i(TAG, "ShortcutManager: throttling counter reset for userId=" + userId);
}
void resetAllThrottlingInner() {
@@ -2755,7 +2755,7 @@
@Override
public void onApplicationActive(String packageName, int userId) {
if (DEBUG) {
- Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
+ Slog.d(TAG, "onApplicationActive: package=" + packageName + " userId=" + userId);
}
enforceResetThrottlingPermission();
synchronized (mServiceLock) {
@@ -2822,7 +2822,7 @@
if (defaultLauncher != null) {
if (DEBUG) {
- Slog.v(TAG, "Detected launcher: " + defaultLauncher + " user: " + userId);
+ Slog.v(TAG, "Detected launcher: " + defaultLauncher + " userId=" + userId);
}
return defaultLauncher.equals(packageName);
} else {
@@ -2875,11 +2875,11 @@
if (defaultLauncher != null) {
if (DEBUG) {
Slog.v(TAG, "Default launcher from RoleManager: " + defaultLauncher
- + " user: " + userId);
+ + " userId=" + userId);
}
user.setCachedLauncher(defaultLauncher);
} else {
- Slog.e(TAG, "Default launcher not found." + " user: " + userId);
+ Slog.e(TAG, "Default launcher not found." + " userId=" + userId);
}
return defaultLauncher;
@@ -2974,7 +2974,7 @@
int queryFlags, int userId, int callingPid, int callingUid) {
if (DEBUG_REBOOT) {
Slog.d(TAG, "Getting shortcuts for launcher= " + callingPackage
- + "user=" + userId + " pkg=" + packageName);
+ + "userId=" + userId + " pkg=" + packageName);
}
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
@@ -3793,7 +3793,7 @@
if (!isUserUnlockedL(userId)) {
if (DEBUG) {
Slog.d(TAG, "Ignoring package broadcast " + action
- + " for locked/stopped user " + userId);
+ + " for locked/stopped userId=" + userId);
}
return;
}
@@ -3814,10 +3814,10 @@
switch (action) {
case Intent.ACTION_PACKAGE_ADDED:
if (replacing) {
- Slog.d(TAG, "replacing package: " + packageName + " userId" + userId);
+ Slog.d(TAG, "replacing package: " + packageName + " userId=" + userId);
handlePackageUpdateFinished(packageName, userId);
} else {
- Slog.d(TAG, "adding package: " + packageName + " userId" + userId);
+ Slog.d(TAG, "adding package: " + packageName + " userId=" + userId);
handlePackageAdded(packageName, userId);
}
break;
@@ -3825,21 +3825,21 @@
if (!replacing || (replacing && archival)) {
if (!replacing) {
Slog.d(TAG, "removing package: "
- + packageName + " userId" + userId);
+ + packageName + " userId=" + userId);
} else if (archival) {
Slog.d(TAG, "archiving package: "
- + packageName + " userId" + userId);
+ + packageName + " userId=" + userId);
}
handlePackageRemoved(packageName, userId);
}
break;
case Intent.ACTION_PACKAGE_CHANGED:
- Slog.d(TAG, "changing package: " + packageName + " userId" + userId);
+ Slog.d(TAG, "changing package: " + packageName + " userId=" + userId);
handlePackageChanged(packageName, userId);
break;
case Intent.ACTION_PACKAGE_DATA_CLEARED:
Slog.d(TAG, "clearing data for package: "
- + packageName + " userId" + userId);
+ + packageName + " userId=" + userId);
handlePackageDataCleared(packageName, userId);
break;
}
@@ -3902,7 +3902,7 @@
if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
if (DEBUG) {
Slog.d(TAG, "Uninstalled: " + spi.getPackageName()
- + " user " + spi.getPackageUserId());
+ + " userId=" + spi.getPackageUserId());
}
gonePackages.add(
UserPackage.of(spi.getPackageUserId(), spi.getPackageName()));
@@ -3927,7 +3927,7 @@
@GuardedBy("mServiceLock")
private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
if (DEBUG_REBOOT) {
- Slog.d(TAG, "rescan updated package user=" + userId + " last scanned=" + lastScanTime);
+ Slog.d(TAG, "rescan updated package userId=" + userId + " last scanned=" + lastScanTime);
}
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -3953,7 +3953,7 @@
private void handlePackageAdded(String packageName, @UserIdInt int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
+ Slog.d(TAG, String.format("handlePackageAdded: %s userId=%d", packageName, userId));
}
synchronized (mServiceLock) {
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -3965,7 +3965,7 @@
private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
+ Slog.d(TAG, String.format("handlePackageUpdateFinished: %s userId=%d",
packageName, userId));
}
synchronized (mServiceLock) {
@@ -3981,7 +3981,7 @@
private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
+ Slog.d(TAG, String.format("handlePackageRemoved: %s userId=%d", packageName,
packageUserId));
}
cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false);
@@ -3991,7 +3991,7 @@
private void handlePackageDataCleared(String packageName, int packageUserId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
+ Slog.d(TAG, String.format("handlePackageDataCleared: %s userId=%d", packageName,
packageUserId));
}
cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true);
@@ -4006,7 +4006,7 @@
return;
}
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName,
+ Slog.d(TAG, String.format("handlePackageChanged: %s userId=%d", packageName,
packageUserId));
}
@@ -4193,7 +4193,7 @@
private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta,
Consumer<ApplicationInfo> callback) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime
+ Slog.d(TAG, "forUpdatedPackages for userId=" + userId + ", lastScanTime=" + lastScanTime
+ " afterOta=" + afterOta);
}
final List<PackageInfo> list = getInstalledPackages(userId);
@@ -4302,7 +4302,7 @@
return mContext.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
.getPackageManager().getResourcesForApplication(packageName);
} catch (NameNotFoundException e) {
- Slog.e(TAG, "Resources of package " + packageName + " for user " + userId
+ Slog.e(TAG, "Resources of package " + packageName + " for userId=" + userId
+ " not found");
return null;
} finally {
@@ -4512,17 +4512,17 @@
public byte[] getBackupPayload(@UserIdInt int userId) {
enforceSystem();
if (DEBUG) {
- Slog.d(TAG, "Backing up user " + userId);
+ Slog.d(TAG, "Backing up user with userId=" + userId);
}
synchronized (mServiceLock) {
if (!isUserUnlockedL(userId)) {
- wtf("Can't backup: user " + userId + " is locked or not running");
+ wtf("Can't backup: userId=" + userId + " is locked or not running");
return null;
}
final ShortcutUser user = getUserShortcutsLocked(userId);
if (user == null) {
- wtf("Can't backup: user not found: id=" + userId);
+ wtf("Can't backup: user not found: userId=" + userId);
return null;
}
@@ -4562,11 +4562,11 @@
public void applyRestore(byte[] payload, @UserIdInt int userId) {
enforceSystem();
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "Restoring user " + userId);
+ Slog.d(TAG, "Restoring user with userId=" + userId);
}
synchronized (mServiceLock) {
if (!isUserUnlockedL(userId)) {
- wtf("Can't restore: user " + userId + " is locked or not running");
+ wtf("Can't restore: user (with userId=" + userId + ") is locked or not running");
return;
}
// Note we print the file timestamps in dumpsys too, but also printing the timestamp
@@ -4989,7 +4989,7 @@
mUserId = UserHandle.parseUserArg(getNextArgRequired());
if (!isUserUnlockedL(mUserId)) {
throw new CommandException(
- "User " + mUserId + " is not running or locked");
+ "User (with userId=" + mUserId + ") is not running or locked");
}
break;
}
@@ -5094,7 +5094,7 @@
synchronized (mServiceLock) {
parseOptionsLocked(/* takeUser =*/ true);
- Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
+ Slog.i(TAG, "cmd: handleResetThrottling: userId=" + mUserId);
resetThrottlingInner(mUserId);
}
@@ -5136,7 +5136,7 @@
final String defaultLauncher = getDefaultLauncher(mUserId);
if (defaultLauncher == null) {
throw new CommandException(
- "Failed to get the default launcher for user " + mUserId);
+ "Failed to get the default launcher for userId=" + mUserId);
}
// Get the class name of the component from PM to keep the old behaviour.
@@ -5157,7 +5157,7 @@
synchronized (mServiceLock) {
parseOptionsLocked(/* takeUser =*/ true);
- Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
+ Slog.i(TAG, "cmd: handleUnloadUser: userId=" + mUserId);
ShortcutService.this.handleStopUser(mUserId);
}
@@ -5168,7 +5168,7 @@
parseOptionsLocked(/* takeUser =*/ true);
final String packageName = getNextArgRequired();
- Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
+ Slog.i(TAG, "cmd: handleClearShortcuts: userId=" + mUserId + ", " + packageName);
ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
/* appStillExists = */ true);
@@ -5180,7 +5180,7 @@
parseOptionsLocked(/* takeUser =*/ true);
final String packageName = getNextArgRequired();
- Slog.i(TAG, "cmd: handleGetShortcuts: user=" + mUserId + ", flags="
+ Slog.i(TAG, "cmd: handleGetShortcuts: userId=" + mUserId + ", flags="
+ mShortcutMatchFlags + ", package=" + packageName);
final ShortcutUser user = ShortcutService.this.getUserShortcutsLocked(mUserId);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 74594cc..94bdfbd 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -56,8 +56,8 @@
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.rollback.ApexdRevertLogger;
import com.android.server.rollback.RollbackManagerInternal;
-import com.android.server.rollback.WatchdogRollbackLogger;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -764,7 +764,7 @@
private void logFailedApexSessionsIfNecessary() {
synchronized (mFailedPackageNames) {
if (!mFailedPackageNames.isEmpty()) {
- WatchdogRollbackLogger.logApexdRevert(mContext,
+ ApexdRevertLogger.logApexdRevert(mContext,
mFailedPackageNames, mNativeFailureReason);
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 1c70af0..5ca5fda 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -397,6 +397,8 @@
ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
| flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
| flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
+ ai.privateFlagsExt |=
+ flag(state.isNotLaunched(), ApplicationInfo.PRIVATE_FLAG_EXT_NOT_LAUNCHED);
if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
} else if (state.getEnabledState()
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index f518769..e9cb279 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -375,24 +375,34 @@
@Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation,
@NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
Boolean, SyncNotedAppOp> superImpl) {
- if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
- final int shellUid = UserHandle.getUid(
- UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID);
- final long identity = Binder.clearCallingIdentity();
- try {
- return superImpl.apply(code,
- new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
- attributionSource.getAttributionTag(),
- attributionSource.getToken(), /*renouncedPermissions*/ null,
- attributionSource.getDeviceId(), attributionSource.getNext()),
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- skiProxyOperation);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ if (!isDelegateOp(code)) {
+ return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage, skiProxyOperation);
}
- return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage, skiProxyOperation);
+
+ final int shellUid = UserHandle.getUid(
+ UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID);
+ AttributionSource next = attributionSource.getNext();
+ if (next != null && next.getUid() == mDelegateAndOwnerUid) {
+ next = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+ next.getAttributionTag(), next.getToken(), /*renouncedPermissions*/ null,
+ next.getDeviceId(), next.getNext());
+ attributionSource = new AttributionSource(attributionSource, next);
+ }
+ if (attributionSource.getUid() == mDelegateAndOwnerUid) {
+ attributionSource = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+ attributionSource.getAttributionTag(),
+ attributionSource.getToken(), /*renouncedPermissions*/ null,
+ attributionSource.getDeviceId(), attributionSource.getNext());
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(code, attributionSource,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skiProxyOperation);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6c78b3c..6e7a009 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1366,7 +1366,7 @@
for (int requestedPermissionNum = 0; requestedPermissionNum < numRequestedPermissions;
requestedPermissionNum++) {
- String permission = requestedPermissions[requestedPermissionNum];
+ String permission = sortedRequestedPermissions[requestedPermissionNum];
// If there is a disabled system app it may request a permission the updated
// version ot the data partition doesn't, In this case skip the permission.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 46e6546..df9f7fb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -26,7 +26,6 @@
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.app.AppOpsManager.OP_BLUETOOTH_CONNECT;
import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED;
import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
import static android.permission.flags.Flags.serverSideAttributionRegistration;
@@ -1640,22 +1639,7 @@
throw new SecurityException(msg + ":" + e.getMessage());
}
}
- int result = Math.max(checkedOpResult, notedOpResult);
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (op == OP_BLUETOOTH_CONNECT && result == MODE_ERRORED) {
- if (result == checkedOpResult) {
- Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
- + " checkOp for resolvedAttributionSource "
- + resolvedAttributionSource + " and op " + op
- + " returned MODE_ERRORED");
- } else {
- Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
- + " noteOp for resolvedAttributionSource "
- + resolvedAttributionSource + " and op " + notedOp
- + " returned MODE_ERRORED");
- }
- }
- return result;
+ return Math.max(checkedOpResult, notedOpResult);
}
}
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 7ed8972..027e69c 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -17,7 +17,9 @@
package com.android.server.policy;
import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
+import static com.android.hardware.input.Flags.modifierShortcutManagerRefactor;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.role.RoleManager;
@@ -37,6 +39,7 @@
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.KeyCharacterMap;
@@ -81,8 +84,8 @@
private static final String ATTRIBUTE_SHIFT = "shift";
private static final String ATTRIBUTE_ROLE = "role";
- private final SparseArray<Intent> mIntentShortcuts = new SparseArray<>();
- private final SparseArray<Intent> mShiftShortcuts = new SparseArray<>();
+ private final SparseArray<Intent> mCategoryShortcuts = new SparseArray<>();
+ private final SparseArray<Intent> mShiftCategoryShortcuts = new SparseArray<>();
private final SparseArray<String> mRoleShortcuts = new SparseArray<String>();
private final SparseArray<String> mShiftRoleShortcuts = new SparseArray<String>();
private final Map<String, Intent> mRoleIntents = new HashMap<String, Intent>();
@@ -127,6 +130,7 @@
private boolean mSearchKeyShortcutPending = false;
private boolean mConsumeSearchKeyUp = true;
private UserHandle mCurrentUser;
+ private final Map<Pair<Character, Boolean>, Bookmark> mBookmarks = new HashMap<>();
ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) {
mContext = context;
@@ -134,7 +138,14 @@
RoleManager rm = mContext.getSystemService(RoleManager.class);
rm.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
(String roleName, UserHandle user) -> {
- mRoleIntents.remove(roleName);
+ if (modifierShortcutManagerRefactor()) {
+ mBookmarks.values().stream().filter(b ->
+ b instanceof RoleBookmark
+ && ((RoleBookmark) b).getRole().equals(roleName))
+ .forEach(Bookmark::clearIntent);
+ } else {
+ mRoleIntents.remove(roleName);
+ }
}, UserHandle.ALL);
mCurrentUser = currentUser;
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -146,8 +157,26 @@
// Role based shortcuts may resolve to different apps for different users
// so clear the cache.
- mRoleIntents.clear();
- mComponentIntents.clear();
+ clearRoleIntents();
+ clearComponentIntents();
+ }
+
+ void clearRoleIntents() {
+ if (modifierShortcutManagerRefactor()) {
+ mBookmarks.values().stream().filter(b ->
+ b instanceof RoleBookmark).forEach(Bookmark::clearIntent);
+ } else {
+ mRoleIntents.clear();
+ }
+ }
+
+ void clearComponentIntents() {
+ if (modifierShortcutManagerRefactor()) {
+ mBookmarks.values().stream().filter(b ->
+ b instanceof ComponentBookmark).forEach(Bookmark::clearIntent);
+ } else {
+ mComponentIntents.clear();
+ }
}
/**
@@ -176,77 +205,111 @@
Intent shortcutIntent = null;
- // If the Shift key is pressed, then search for the shift shortcuts.
- SparseArray<Intent> shortcutMap = isShiftOn ? mShiftShortcuts : mIntentShortcuts;
-
// First try the exact keycode (with modifiers).
int shortcutChar = kcm.get(keyCode, metaState);
if (shortcutChar == 0) {
return null;
}
- shortcutIntent = shortcutMap.get(shortcutChar);
- if (shortcutIntent == null) {
- // Next try the primary character on that key.
- shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
- if (shortcutChar == 0) {
- return null;
+ if (modifierShortcutManagerRefactor()) {
+ Bookmark bookmark = mBookmarks.get(new Pair<>((char) shortcutChar, isShiftOn));
+ if (bookmark == null) {
+ // Next try the primary character on that key.
+ shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
+ if (shortcutChar == 0) {
+ return null;
+ }
+ bookmark = mBookmarks.get(new Pair<>((char) shortcutChar, isShiftOn));
}
+
+ if (bookmark != null) {
+ Context context = modifierShortcutManagerMultiuser()
+ ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+ shortcutIntent = bookmark.getIntent(context);
+ } else {
+ Log.d(TAG, "No bookmark found for "
+ + (isShiftOn ? "SHIFT+" : "") + (char) shortcutChar);
+ }
+ } else {
+ // If the Shift key is pressed, then search for the shift shortcuts.
+ SparseArray<Intent> shortcutMap = isShiftOn
+ ? mShiftCategoryShortcuts : mCategoryShortcuts;
shortcutIntent = shortcutMap.get(shortcutChar);
- }
- if (shortcutIntent == null) {
- // Next check for role based shortcut with primary character.
- String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
- : mRoleShortcuts.get(shortcutChar);
- if (role != null) {
- shortcutIntent = getRoleLaunchIntent(role);
- }
- }
-
- if (modifierShortcutManagerMultiuser()) {
if (shortcutIntent == null) {
- // Next check component based shortcuts with primary character.
- ComponentName component = isShiftOn
- ? mShiftComponentShortcuts.get(shortcutChar)
- : mComponentShortcuts.get(shortcutChar);
- if (component != null) {
- shortcutIntent = resolveComponentNameIntent(component);
+ // Next try the primary character on that key.
+ shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
+ if (shortcutChar == 0) {
+ return null;
+ }
+ shortcutIntent = shortcutMap.get(shortcutChar);
+ }
+
+ if (shortcutIntent == null) {
+ // Next check for role based shortcut with primary character.
+ String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
+ : mRoleShortcuts.get(shortcutChar);
+ if (role != null) {
+ shortcutIntent = getRoleLaunchIntent(role);
+ }
+ }
+
+ if (modifierShortcutManagerMultiuser()) {
+ if (shortcutIntent == null) {
+ // Next check component based shortcuts with primary character.
+ ComponentName component = isShiftOn
+ ? mShiftComponentShortcuts.get(shortcutChar)
+ : mComponentShortcuts.get(shortcutChar);
+ if (component != null) {
+ shortcutIntent = resolveComponentNameIntent(component);
+ }
}
}
}
return shortcutIntent;
}
+ @Nullable
+ private static Intent getRoleLaunchIntent(Context context, String role) {
+ Intent intent = null;
+ RoleManager rm = context.getSystemService(RoleManager.class);
+ PackageManager pm = context.getPackageManager();
+ if (rm.isRoleAvailable(role)) {
+ String rolePackage = rm.getDefaultApplication(role);
+ if (rolePackage != null) {
+ intent = pm.getLaunchIntentForPackage(rolePackage);
+ if (intent != null) {
+ intent.putExtra(EXTRA_ROLE, role);
+
+ } else {
+ Log.w(TAG, "No launch intent for role " + role);
+ }
+ } else {
+ Log.w(TAG, "No default application for role "
+ + role + " user=" + context.getUser());
+ }
+ } else {
+ Log.w(TAG, "Role " + role + " is not available.");
+ }
+ return intent;
+ }
+
+ @Nullable
private Intent getRoleLaunchIntent(String role) {
Intent intent = mRoleIntents.get(role);
if (intent == null) {
Context context = modifierShortcutManagerMultiuser()
? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
- RoleManager rm = context.getSystemService(RoleManager.class);
- PackageManager pm = context.getPackageManager();
- if (rm.isRoleAvailable(role)) {
- String rolePackage = rm.getDefaultApplication(role);
- if (rolePackage != null) {
- intent = pm.getLaunchIntentForPackage(rolePackage);
- if (intent != null) {
- intent.putExtra(EXTRA_ROLE, role);
- mRoleIntents.put(role, intent);
- } else {
- Log.w(TAG, "No launch intent for role " + role);
- }
- } else {
- Log.w(TAG, "No default application for role " + role);
- }
- } else {
- Log.w(TAG, "Role " + role + " is not available.");
+ intent = getRoleLaunchIntent(context, role);
+ if (intent != null) {
+ mRoleIntents.put(role, intent);
}
}
+
return intent;
}
private void loadShortcuts() {
-
try {
XmlResourceParser parser = mContext.getResources().getXml(R.xml.bookmarks);
XmlUtils.beginDocument(parser, TAG_BOOKMARKS);
@@ -276,57 +339,84 @@
continue;
}
- final int shortcutChar = shortcutName.charAt(0);
final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true"));
- final Intent intent;
- if (packageName != null && className != null) {
- if (roleName != null || categoryName != null) {
- Log.w(TAG, "Cannot specify role or category when package and class"
- + " are present for bookmark packageName=" + packageName
- + " className=" + className + " shortcutChar=" + shortcutChar);
- continue;
+
+ if (modifierShortcutManagerRefactor()) {
+ final char shortcutChar = shortcutName.charAt(0);
+ Bookmark bookmark = null;
+ if (packageName != null && className != null) {
+ bookmark = new ComponentBookmark(
+ shortcutChar, isShiftShortcut, packageName, className);
+ } else if (categoryName != null) {
+ bookmark = new CategoryBookmark(
+ shortcutChar, isShiftShortcut, categoryName);
+ } else if (roleName != null) {
+ bookmark = new RoleBookmark(shortcutChar, isShiftShortcut, roleName);
}
- if (modifierShortcutManagerMultiuser()) {
- ComponentName componentName = new ComponentName(packageName, className);
- if (isShiftShortcut) {
- mShiftComponentShortcuts.put(shortcutChar, componentName);
+ if (bookmark != null) {
+ Log.d(TAG, "adding shortcut " + bookmark + "shift="
+ + isShiftShortcut + " char=" + shortcutChar);
+ mBookmarks.put(new Pair<>(shortcutChar, isShiftShortcut), bookmark);
+ }
+ } else {
+ final int shortcutChar = shortcutName.charAt(0);
+ if (packageName != null && className != null) {
+ if (roleName != null || categoryName != null) {
+ Log.w(TAG, "Cannot specify role or category when package and class"
+ + " are present for bookmark packageName=" + packageName
+ + " className=" + className + " shortcutChar=" + shortcutChar);
+ continue;
+ }
+ if (modifierShortcutManagerMultiuser()) {
+ ComponentName componentName =
+ new ComponentName(packageName, className);
+ if (isShiftShortcut) {
+ mShiftComponentShortcuts.put(shortcutChar, componentName);
+ } else {
+ mComponentShortcuts.put(shortcutChar, componentName);
+ }
} else {
- mComponentShortcuts.put(shortcutChar, componentName);
+ Intent intent = resolveComponentNameIntent(packageName, className);
+ if (isShiftShortcut) {
+ mShiftCategoryShortcuts.put(shortcutChar, intent);
+ } else {
+ mCategoryShortcuts.put(shortcutChar, intent);
+ }
+ }
+ continue;
+ } else if (categoryName != null) {
+ if (roleName != null) {
+ Log.w(TAG, "Cannot specify role bookmark when category is present for"
+ + " bookmark shortcutChar=" + shortcutChar
+ + " category= " + categoryName);
+ continue;
+ }
+ Intent intent = Intent.makeMainSelectorActivity(
+ Intent.ACTION_MAIN, categoryName);
+ if (intent == null) {
+ Log.w(TAG, "Null selector intent for " + categoryName);
+ } else {
+ if (isShiftShortcut) {
+ mShiftCategoryShortcuts.put(shortcutChar, intent);
+ } else {
+ mCategoryShortcuts.put(shortcutChar, intent);
+ }
+ }
+ continue;
+ } else if (roleName != null) {
+ // We can't resolve the role at the time of this file being parsed as the
+ // device hasn't finished booting, so we will look it up lazily.
+ if (isShiftShortcut) {
+ mShiftRoleShortcuts.put(shortcutChar, roleName);
+ } else {
+ mRoleShortcuts.put(shortcutChar, roleName);
}
continue;
} else {
- intent = resolveComponentNameIntent(packageName, className);
- }
- } else if (categoryName != null) {
- if (roleName != null) {
- Log.w(TAG, "Cannot specify role bookmark when category is present for"
- + " bookmark shortcutChar=" + shortcutChar
- + " category= " + categoryName);
+ Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName
+ + ": missing package/class, category or role attributes");
continue;
}
- intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName);
- if (intent == null) {
- Log.w(TAG, "Null selector intent for " + categoryName);
- }
- } else if (roleName != null) {
- // We can't resolve the role at the time of this file being parsed as the
- // device hasn't finished booting, so we will look it up lazily.
- if (isShiftShortcut) {
- mShiftRoleShortcuts.put(shortcutChar, roleName);
- } else {
- mRoleShortcuts.put(shortcutChar, roleName);
- }
- continue;
- } else {
- Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName
- + ": missing package/class, category or role attributes");
- continue;
- }
-
- if (isShiftShortcut) {
- mShiftShortcuts.put(shortcutChar, intent);
- } else {
- mIntentShortcuts.put(shortcutChar, intent);
}
}
} catch (XmlPullParserException | IOException e) {
@@ -336,21 +426,35 @@
@Nullable
private Intent resolveComponentNameIntent(ComponentName componentName) {
- Intent intent = mComponentIntents.get(componentName);
- if (intent == null) {
- intent = resolveComponentNameIntent(
- componentName.getPackageName(), componentName.getClassName());
- if (intent != null) {
- mComponentIntents.put(componentName, intent);
+ if (modifierShortcutManagerRefactor()) {
+ return null;
+ } else {
+ Intent intent = mComponentIntents.get(componentName);
+ if (intent == null) {
+ intent = resolveComponentNameIntent(
+ componentName.getPackageName(), componentName.getClassName());
+ if (intent != null) {
+ mComponentIntents.put(componentName, intent);
+ }
}
+ return intent;
}
- return intent;
}
@Nullable
private Intent resolveComponentNameIntent(String packageName, String className) {
- Context context = modifierShortcutManagerMultiuser()
- ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+ if (modifierShortcutManagerRefactor()) {
+ return null;
+ } else {
+ Context context = modifierShortcutManagerMultiuser()
+ ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+ return resolveComponentNameIntent(context, packageName, className);
+ }
+ }
+
+ @Nullable
+ private static Intent resolveComponentNameIntent(
+ Context context, String packageName, String className) {
PackageManager pm = context.getPackageManager();
int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
if (!modifierShortcutManagerMultiuser()) {
@@ -562,64 +666,81 @@
*/
public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
List<KeyboardShortcutInfo> shortcuts = new ArrayList();
- for (int i = 0; i < mIntentShortcuts.size(); i++) {
- KeyboardShortcutInfo info = shortcutInfoFromIntent(
- (char) (mIntentShortcuts.keyAt(i)), mIntentShortcuts.valueAt(i), false);
- if (info != null) {
- shortcuts.add(info);
- }
- }
-
- for (int i = 0; i < mShiftShortcuts.size(); i++) {
- KeyboardShortcutInfo info = shortcutInfoFromIntent(
- (char) (mShiftShortcuts.keyAt(i)), mShiftShortcuts.valueAt(i), true);
- if (info != null) {
- shortcuts.add(info);
- }
- }
-
- for (int i = 0; i < mRoleShortcuts.size(); i++) {
- String role = mRoleShortcuts.valueAt(i);
- KeyboardShortcutInfo info = shortcutInfoFromIntent(
- (char) (mRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), false);
- if (info != null) {
- shortcuts.add(info);
- }
- }
-
- for (int i = 0; i < mShiftRoleShortcuts.size(); i++) {
- String role = mShiftRoleShortcuts.valueAt(i);
- KeyboardShortcutInfo info = shortcutInfoFromIntent(
- (char) (mShiftRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), true);
- if (info != null) {
- shortcuts.add(info);
- }
- }
-
- if (modifierShortcutManagerMultiuser()) {
- for (int i = 0; i < mComponentShortcuts.size(); i++) {
- ComponentName component = mComponentShortcuts.valueAt(i);
+ if (modifierShortcutManagerRefactor()) {
+ for (Bookmark b : mBookmarks.values()) {
KeyboardShortcutInfo info = shortcutInfoFromIntent(
- (char) (mComponentShortcuts.keyAt(i)),
- resolveComponentNameIntent(component),
+ b.getShortcutChar(), b.getIntent(mContext), b.isShift());
+ if (info != null) {
+ shortcuts.add(info);
+ }
+ }
+ } else {
+ for (int i = 0; i < mCategoryShortcuts.size(); i++) {
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(
+ (char) (mCategoryShortcuts.keyAt(i)),
+ mCategoryShortcuts.valueAt(i),
false);
if (info != null) {
shortcuts.add(info);
}
}
- for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
- ComponentName component = mShiftComponentShortcuts.valueAt(i);
+ for (int i = 0; i < mShiftCategoryShortcuts.size(); i++) {
KeyboardShortcutInfo info = shortcutInfoFromIntent(
- (char) (mShiftComponentShortcuts.keyAt(i)),
- resolveComponentNameIntent(component),
+ (char) (mShiftCategoryShortcuts.keyAt(i)),
+ mShiftCategoryShortcuts.valueAt(i),
true);
if (info != null) {
shortcuts.add(info);
}
}
- }
+ for (int i = 0; i < mRoleShortcuts.size(); i++) {
+ String role = mRoleShortcuts.valueAt(i);
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(
+ (char) (mRoleShortcuts.keyAt(i)),
+ getRoleLaunchIntent(role),
+ false);
+ if (info != null) {
+ shortcuts.add(info);
+ }
+ }
+
+ for (int i = 0; i < mShiftRoleShortcuts.size(); i++) {
+ String role = mShiftRoleShortcuts.valueAt(i);
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(
+ (char) (mShiftRoleShortcuts.keyAt(i)),
+ getRoleLaunchIntent(role),
+ true);
+ if (info != null) {
+ shortcuts.add(info);
+ }
+ }
+
+ if (modifierShortcutManagerMultiuser()) {
+ for (int i = 0; i < mComponentShortcuts.size(); i++) {
+ ComponentName component = mComponentShortcuts.valueAt(i);
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(
+ (char) (mComponentShortcuts.keyAt(i)),
+ resolveComponentNameIntent(component),
+ false);
+ if (info != null) {
+ shortcuts.add(info);
+ }
+ }
+
+ for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
+ ComponentName component = mShiftComponentShortcuts.valueAt(i);
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(
+ (char) (mShiftComponentShortcuts.keyAt(i)),
+ resolveComponentNameIntent(component),
+ true);
+ if (info != null) {
+ shortcuts.add(info);
+ }
+ }
+ }
+ }
return new KeyboardShortcutGroup(
mContext.getString(R.string.keyboard_shortcut_group_applications),
shortcuts);
@@ -800,57 +921,171 @@
void dump(String prefix, PrintWriter pw) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", prefix);
ipw.println("ModifierShortcutManager shortcuts:");
-
- ipw.increaseIndent();
- ipw.println("Roles");
- ipw.increaseIndent();
- for (int i = 0; i < mRoleShortcuts.size(); i++) {
- String role = mRoleShortcuts.valueAt(i);
- char shortcutChar = (char) mRoleShortcuts.keyAt(i);
- Intent intent = getRoleLaunchIntent(role);
- ipw.println(shortcutChar + " " + role + " " + intent);
- }
-
- for (int i = 0; i < mShiftRoleShortcuts.size(); i++) {
- String role = mShiftRoleShortcuts.valueAt(i);
- char shortcutChar = (char) mShiftRoleShortcuts.keyAt(i);
- Intent intent = getRoleLaunchIntent(role);
- ipw.println("SHIFT+" + shortcutChar + " " + role + " " + intent);
- }
-
- ipw.decreaseIndent();
- ipw.println("Selectors");
- ipw.increaseIndent();
- for (int i = 0; i < mIntentShortcuts.size(); i++) {
- char shortcutChar = (char) mIntentShortcuts.keyAt(i);
- Intent intent = mIntentShortcuts.valueAt(i);
- ipw.println(shortcutChar + " " + intent);
- }
-
- for (int i = 0; i < mShiftShortcuts.size(); i++) {
- char shortcutChar = (char) mShiftShortcuts.keyAt(i);
- Intent intent = mShiftShortcuts.valueAt(i);
- ipw.println("SHIFT+" + shortcutChar + " " + intent);
-
- }
-
- if (modifierShortcutManagerMultiuser()) {
- ipw.decreaseIndent();
- ipw.println("ComponentNames");
+ if (modifierShortcutManagerRefactor()) {
ipw.increaseIndent();
- for (int i = 0; i < mComponentShortcuts.size(); i++) {
- char shortcutChar = (char) mComponentShortcuts.keyAt(i);
- ComponentName component = mComponentShortcuts.valueAt(i);
- Intent intent = resolveComponentNameIntent(component);
- ipw.println(shortcutChar + " " + component + " " + intent);
+ for (Bookmark b : mBookmarks.values()) {
+ boolean isShift = b.isShift();
+ char shortcutChar = b.getShortcutChar();
+ Context context = modifierShortcutManagerMultiuser()
+ ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+
+ Intent intent = b.getIntent(context);
+ ipw.print(isShift ? "SHIFT+" : "");
+ ipw.println(shortcutChar + " " + intent);
+ ipw.increaseIndent();
+ ipw.increaseIndent();
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(shortcutChar, intent, isShift);
+ if (info != null) {
+ ipw.println("Resolves to: " + info.getLabel());
+ } else {
+ ipw.println("<No KeyboardShortcutInfo available for this shortcut>");
+ }
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ }
+ } else {
+ ipw.increaseIndent();
+ ipw.println("Roles");
+ ipw.increaseIndent();
+ for (int i = 0; i < mRoleShortcuts.size(); i++) {
+ String role = mRoleShortcuts.valueAt(i);
+ char shortcutChar = (char) mRoleShortcuts.keyAt(i);
+ Intent intent = getRoleLaunchIntent(role);
+ ipw.println(shortcutChar + " " + role + " " + intent);
}
- for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
- char shortcutChar = (char) mShiftComponentShortcuts.keyAt(i);
- ComponentName component = mShiftComponentShortcuts.valueAt(i);
- Intent intent = resolveComponentNameIntent(component);
- ipw.println("SHIFT+" + shortcutChar + " " + component + " " + intent);
+ for (int i = 0; i < mShiftRoleShortcuts.size(); i++) {
+ String role = mShiftRoleShortcuts.valueAt(i);
+ char shortcutChar = (char) mShiftRoleShortcuts.keyAt(i);
+ Intent intent = getRoleLaunchIntent(role);
+ ipw.println("SHIFT+" + shortcutChar + " " + role + " " + intent);
}
+
+ ipw.decreaseIndent();
+ ipw.println("Selectors");
+ ipw.increaseIndent();
+ for (int i = 0; i < mCategoryShortcuts.size(); i++) {
+ char shortcutChar = (char) mCategoryShortcuts.keyAt(i);
+ Intent intent = mCategoryShortcuts.valueAt(i);
+ ipw.println(shortcutChar + " " + intent);
+ }
+
+ for (int i = 0; i < mShiftCategoryShortcuts.size(); i++) {
+ char shortcutChar = (char) mShiftCategoryShortcuts.keyAt(i);
+ Intent intent = mShiftCategoryShortcuts.valueAt(i);
+ ipw.println("SHIFT+" + shortcutChar + " " + intent);
+
+ }
+
+ if (modifierShortcutManagerMultiuser()) {
+ ipw.decreaseIndent();
+ ipw.println("ComponentNames");
+ ipw.increaseIndent();
+ for (int i = 0; i < mComponentShortcuts.size(); i++) {
+ char shortcutChar = (char) mComponentShortcuts.keyAt(i);
+ ComponentName component = mComponentShortcuts.valueAt(i);
+ Intent intent = resolveComponentNameIntent(component);
+ ipw.println(shortcutChar + " " + component + " " + intent);
+ }
+
+ for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
+ char shortcutChar = (char) mShiftComponentShortcuts.keyAt(i);
+ ComponentName component = mShiftComponentShortcuts.valueAt(i);
+ Intent intent = resolveComponentNameIntent(component);
+ ipw.println("SHIFT+" + shortcutChar + " " + component + " " + intent);
+ }
+ }
+ }
+ }
+
+ private abstract static class Bookmark {
+ private final char mShortcutChar;
+ private final boolean mShift;
+ protected Intent mIntent;
+
+ Bookmark(char shortcutChar, boolean shift) {
+ mShortcutChar = shortcutChar;
+ mShift = shift;
+ }
+
+ public char getShortcutChar() {
+ return mShortcutChar;
+ }
+
+ public boolean isShift() {
+ return mShift;
+ }
+
+ public abstract Intent getIntent(Context context);
+
+ public void clearIntent() {
+ mIntent = null;
+ }
+
+ }
+
+ private static final class RoleBookmark extends Bookmark {
+ private final String mRole;
+
+ RoleBookmark(char shortcutChar, boolean shift, String role) {
+ super(shortcutChar, shift);
+ mRole = role;
+ }
+
+ public String getRole() {
+ return mRole;
+ }
+
+ @Nullable
+ @Override
+ public Intent getIntent(Context context) {
+ if (mIntent != null) {
+ return mIntent;
+ }
+ mIntent = getRoleLaunchIntent(context, mRole);
+ return mIntent;
+ }
+ }
+
+ private static final class CategoryBookmark extends Bookmark {
+ private final String mCategory;
+
+ CategoryBookmark(char shortcutChar, boolean shift, String category) {
+ super(shortcutChar, shift);
+ mCategory = category;
+ }
+
+ @NonNull
+ @Override
+ public Intent getIntent(Context context) {
+ if (mIntent != null) {
+ return mIntent;
+ }
+
+ mIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, mCategory);
+ return mIntent;
+ }
+ }
+
+ private static final class ComponentBookmark extends Bookmark {
+ private final String mPackageName;
+ private final String mClassName;
+
+ ComponentBookmark(
+ char shortcutChar, boolean shift, String packageName, String className) {
+ super(shortcutChar, shift);
+ mPackageName = packageName;
+ mClassName = className;
+ }
+
+ @Nullable
+ @Override
+ public Intent getIntent(Context context) {
+ if (mIntent != null) {
+ return mIntent;
+ }
+ mIntent = resolveComponentNameIntent(context, mPackageName, mClassName);
+ return mIntent;
}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f96706e..ed9dcfa 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -16,6 +16,7 @@
package com.android.server.policy;
+import static android.Manifest.permission.CREATE_VIRTUAL_DEVICE;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
@@ -59,13 +60,18 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
@@ -529,7 +535,7 @@
volatile boolean mRequestedOrSleepingDefaultDisplay;
/**
- * This is used to check whether to invoke {@link #updateScreenOffSleepToken} when screen is
+ * This is used to check whether to acquire screen-off sleep token when screen is
* turned off. E.g. if it is false when screen is turned off and the display is swapping, it
* is expected that the screen will be on in a short time. Then it is unnecessary to acquire
* screen-off-sleep-token, so it can avoid intermediate visibility or lifecycle changes.
@@ -604,7 +610,6 @@
private boolean mPendingKeyguardOccluded;
private boolean mKeyguardOccludedChanged;
- private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
@@ -804,7 +809,7 @@
event.recycle();
break;
case MSG_HANDLE_ALL_APPS:
- launchAllAppsAction();
+ launchAllAppsAction((KeyEvent) msg.obj);
break;
case MSG_RINGER_TOGGLE_CHORD:
handleRingerChordGesture();
@@ -1873,26 +1878,31 @@
}
}
- private void launchAllAppsAction() {
- Intent intent = new Intent(Intent.ACTION_ALL_APPS);
- if (mHasFeatureLeanback) {
- Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
- intentLauncher.addCategory(Intent.CATEGORY_HOME);
- ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher,
- PackageManager.MATCH_SYSTEM_ONLY,
- mCurrentUserId);
- if (resolveInfo != null) {
- intent.setPackage(resolveInfo.activityInfo.packageName);
+ private void launchAllAppsAction(KeyEvent event) {
+ if (mHasFeatureLeanback || mHasFeatureWatch) {
+ // TV and watch support the all apps intent
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
+ Intent intent = new Intent(Intent.ACTION_ALL_APPS);
+ if (mHasFeatureLeanback) {
+ Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
+ intentLauncher.addCategory(Intent.CATEGORY_HOME);
+ ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher,
+ PackageManager.MATCH_SYSTEM_ONLY,
+ mCurrentUserId);
+ if (resolveInfo != null) {
+ intent.setPackage(resolveInfo.activityInfo.packageName);
+ }
}
- }
- startActivityAsUser(intent, UserHandle.CURRENT);
- }
-
- private void launchAllAppsViaA11y() {
- AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
- if (accessibilityManager != null) {
- accessibilityManager.performSystemAction(
- AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+ startActivityAsUser(intent, UserHandle.CURRENT);
+ } else {
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
+ AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
+ if (accessibilityManager != null) {
+ accessibilityManager.performSystemAction(
+ AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+ }
}
dismissKeyboardShortcutsMenu();
}
@@ -2070,15 +2080,7 @@
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, "Home - Long Press");
switch (mLongPressOnHomeBehavior) {
case LONG_PRESS_HOME_ALL_APPS:
- if (mHasFeatureLeanback) {
- launchAllAppsAction();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
- } else {
- launchAllAppsViaA11y();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
- }
+ launchAllAppsAction(event);
break;
case LONG_PRESS_HOME_ASSIST:
notifyKeyGestureCompleted(event,
@@ -2217,9 +2219,6 @@
mLockPatternUtils = new LockPatternUtils(mContext);
mLogger = new MetricsLogger();
- mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
- .createSleepTokenAcquirer("ScreenOff");
-
Resources res = mContext.getResources();
mWakeOnDpadKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
@@ -3058,7 +3057,7 @@
/** {@inheritDoc} */
@Override
public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
- int[] outAppOp) {
+ int[] outAppOp, int displayId) {
if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
!= PERMISSION_GRANTED) {
return ADD_PERMISSION_DENIED;
@@ -3098,6 +3097,12 @@
case TYPE_VOICE_INTERACTION:
case TYPE_QS_DIALOG:
case TYPE_NAVIGATION_BAR_PANEL:
+ case TYPE_STATUS_BAR:
+ case TYPE_NOTIFICATION_SHADE:
+ case TYPE_NAVIGATION_BAR:
+ case TYPE_STATUS_BAR_ADDITIONAL:
+ case TYPE_STATUS_BAR_SUB_PANEL:
+ case TYPE_VOICE_INTERACTION_STARTING:
// The window manager will check these.
return ADD_OKAY;
}
@@ -3141,6 +3146,13 @@
return ADD_OKAY;
}
+ // Allow virtual device owners to add overlays on the displays they own.
+ if (mWindowManagerFuncs.isCallerVirtualDeviceOwner(displayId, callingUid)
+ && mContext.checkCallingOrSelfPermission(CREATE_VIRTUAL_DEVICE)
+ == PERMISSION_GRANTED) {
+ return ADD_OKAY;
+ }
+
// check if user has enabled this operation. SecurityException will be thrown if this app
// has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
// make sure the usage is logged.
@@ -3581,18 +3593,6 @@
if (down) {
int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;
- // Disable autobrightness if it's on
- int auto = Settings.System.getIntForUser(
- mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
- UserHandle.USER_CURRENT_OR_SELF);
- if (auto != 0) {
- Settings.System.putIntForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
- UserHandle.USER_CURRENT_OR_SELF);
- }
int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
float minLinearBrightness = mPowerManager.getBrightnessConstraint(
@@ -3688,18 +3688,11 @@
break;
case KeyEvent.KEYCODE_ALL_APPS:
if (firstDown) {
- if (mHasFeatureLeanback) {
- mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
- Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
- msg.setAsynchronous(true);
- msg.sendToTarget();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
- } else {
- launchAllAppsViaA11y();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
- }
+ mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
+
+ Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS, new KeyEvent(event));
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
@@ -3752,9 +3745,7 @@
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
} else if (mPendingMetaAction) {
if (!canceled) {
- launchAllAppsViaA11y();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
+ launchAllAppsAction(event);
}
mPendingMetaAction = false;
}
@@ -5526,13 +5517,6 @@
mRequestedOrSleepingDefaultDisplay = true;
mIsGoingToSleepDefaultDisplay = true;
- // In case startedGoingToSleep is called after screenTurnedOff (the source caller is in
- // order but the methods run on different threads) and updateScreenOffSleepToken was
- // skipped. Then acquire sleep token if screen was off.
- if (!mDefaultDisplayPolicy.isScreenOnFully() && !mDefaultDisplayPolicy.isScreenOnEarly()) {
- updateScreenOffSleepToken(true /* acquire */);
- }
-
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
}
@@ -5693,11 +5677,9 @@
if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
if (displayId == DEFAULT_DISPLAY) {
- if (!isSwappingDisplay || mIsGoingToSleepDefaultDisplay) {
- updateScreenOffSleepToken(true /* acquire */);
- }
+ final boolean acquireSleepToken = !isSwappingDisplay || mIsGoingToSleepDefaultDisplay;
mRequestedOrSleepingDefaultDisplay = false;
- mDefaultDisplayPolicy.screenTurnedOff();
+ mDefaultDisplayPolicy.screenTurnedOff(acquireSleepToken);
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurnedOff();
@@ -5753,7 +5735,6 @@
if (displayId == DEFAULT_DISPLAY) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
0 /* cookie */);
- updateScreenOffSleepToken(false /* acquire */);
mDefaultDisplayPolicy.screenTurningOn(screenOnListener);
mBootAnimationDismissable = false;
@@ -6260,15 +6241,6 @@
}
}
- // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
- private void updateScreenOffSleepToken(boolean acquire) {
- if (acquire) {
- mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY);
- } else {
- mScreenOffSleepTokenAcquirer.release(DEFAULT_DISPLAY);
- }
- }
-
/** {@inheritDoc} */
@Override
public void enableScreenAfterBoot() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 989c8a8..892af6b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -362,6 +362,12 @@
* Invoked when a screenshot is taken of the given display to notify registered listeners.
*/
List<ComponentName> notifyScreenshotListeners(int displayId);
+
+ /**
+ * Returns whether the given UID is the owner of a virtual device, which the given display
+ * belongs to.
+ */
+ boolean isCallerVirtualDeviceOwner(int displayId, int callingUid);
}
/**
@@ -421,6 +427,7 @@
* @param packageName package name
* @param outAppOp First element will be filled with the app op corresponding to
* this window, or OP_NONE.
+ * @param displayId The display on which this window is being added.
*
* @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed;
* else an error code, usually
@@ -429,7 +436,7 @@
* @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
*/
int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
- int[] outAppOp);
+ int[] outAppOp, int displayId);
/**
* After the window manager has computed the current configuration based
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 1a2a196..303828f 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -1064,9 +1064,9 @@
private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled,
int ownerUid, int ownerPid, int flags, WorkSource workSource, String packageName,
String historyTag) {
+ long currentTime = mInjector.currentTimeMillis();
mHandler.post(() -> {
if (mFlags.improveWakelockLatency()) {
- long currentTime = mInjector.currentTimeMillis();
if (isEnabled) {
notifyWakelockAcquisition(tag, ownerUid, ownerPid, flags,
workSource, packageName, historyTag, currentTime);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 12e7fd0..71cb882 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3668,7 +3668,7 @@
mBrightWhenDozingConfig);
int wakefulness = powerGroup.getWakefulnessLocked();
if (DEBUG_SPEW) {
- Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
+ Slog.d(TAG, "updatePowerGroupsLocked: displayReady=" + ready
+ ", groupId=" + groupId
+ ", policy=" + policyToString(powerGroup.getPolicyLocked())
+ ", mWakefulness="
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 822ec2e..6847a5c 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -54,6 +54,7 @@
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.Temperature;
+import android.os.Trace;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
@@ -247,6 +248,7 @@
private void setStatusLocked(int newStatus) {
if (newStatus != mStatus) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "ThermalManagerService.status", newStatus);
mStatus = newStatus;
notifyStatusListenersLocked();
}
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index 968ff59..eda222e 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.PowerManager;
+import android.os.Process;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -122,6 +123,9 @@
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ @VisibleForTesting
+ static final String SYSTEM_PACKAGE_NAME = "System";
+
/**
* Lock protects WakeLockLog.dump (binder thread) from conflicting with changes to the log
* happening on the background thread.
@@ -516,21 +520,26 @@
return;
}
- String[] packages;
- if (uidToPackagesCache.contains(tag.ownerUid)) {
- packages = uidToPackagesCache.get(tag.ownerUid);
- } else {
- packages = packageManager.getPackagesForUid(tag.ownerUid);
- uidToPackagesCache.put(tag.ownerUid, packages);
+ if (tag.ownerUid == Process.SYSTEM_UID) {
+ packageName = SYSTEM_PACKAGE_NAME;
}
+ else {
+ String[] packages;
+ if (uidToPackagesCache.contains(tag.ownerUid)) {
+ packages = uidToPackagesCache.get(tag.ownerUid);
+ } else {
+ packages = packageManager.getPackagesForUid(tag.ownerUid);
+ uidToPackagesCache.put(tag.ownerUid, packages);
+ }
- if (packages != null && packages.length > 0) {
- packageName = packages[0];
- if (packages.length > 1) {
- StringBuilder sb = new StringBuilder();
- sb.append(packageName)
- .append(",...");
- packageName = sb.toString();
+ if (packages != null && packages.length > 0) {
+ packageName = packages[0];
+ if (packages.length > 1) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(packageName)
+ .append(",...");
+ packageName = sb.toString();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java
deleted file mode 100644
index 7ba4330..0000000
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power.stats;
-
-import android.util.IndentingPrintWriter;
-
-import com.android.modules.utils.TypedXmlSerializer;
-
-import java.io.IOException;
-
-class AggregatedPowerStatsSection extends PowerStatsSpan.Section {
- public static final String TYPE = "aggregated-power-stats";
-
- private final AggregatedPowerStats mAggregatedPowerStats;
-
- AggregatedPowerStatsSection(AggregatedPowerStats aggregatedPowerStats) {
- super(TYPE);
- mAggregatedPowerStats = aggregatedPowerStats;
- }
-
- public AggregatedPowerStats getAggregatedPowerStats() {
- return mAggregatedPowerStats;
- }
-
- @Override
- void write(TypedXmlSerializer serializer) throws IOException {
- mAggregatedPowerStats.writeXml(serializer);
- }
-
- @Override
- public void dump(IndentingPrintWriter ipw) {
- mAggregatedPowerStats.dump(ipw);
- }
-}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 385561d..680b1ac 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -23,8 +23,6 @@
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
-import static com.android.server.power.stats.MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -149,6 +147,7 @@
import com.android.server.LocalServices;
import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
import libcore.util.EmptyArray;
@@ -180,7 +179,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
@@ -2028,7 +2026,7 @@
void setContext(Context context) {
mPackageManager = context.getPackageManager();
mConsumedEnergyRetriever = new PowerStatsCollector.ConsumedEnergyRetrieverImpl(
- LocalServices.getService(PowerStatsInternal.class));
+ LocalServices.getService(PowerStatsInternal.class), () -> mBatteryVoltageMv);
mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
mTelephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2083,11 +2081,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> mBatteryVoltageMv;
- }
-
- @Override
public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
return mScreenUsageTimeRetriever;
}
@@ -13191,7 +13184,8 @@
final int freq = deltaInfo.getSpecificInfoFrequencyRange(index);
// Map RadioAccessNetworkType to course grain RadioAccessTechnology.
- final int ratBucket = mapRadioAccessNetworkTypeToRadioAccessTechnology(rat);
+ final int ratBucket = MobileRadioPowerStatsLayout
+ .mapRadioAccessNetworkTypeToRadioAccessTechnology(rat);
final RadioAccessTechnologyBatteryStats ratStats = getRatBatteryStatsLocked(
ratBucket);
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index d51cfea..87a3e5e 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.SensorManager;
import android.os.BatteryConsumer;
@@ -27,7 +28,6 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
@@ -44,8 +44,7 @@
public class BatteryUsageStatsProvider {
private static final String TAG = "BatteryUsageStatsProv";
private final Context mContext;
- private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray();
- private final PowerStatsExporter mPowerStatsExporter;
+ private final PowerAttributor mPowerAttributor;
private final PowerStatsStore mPowerStatsStore;
private final PowerProfile mPowerProfile;
private final CpuScalingPolicies mCpuScalingPolicies;
@@ -53,16 +52,18 @@
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
- public BatteryUsageStatsProvider(Context context,
- PowerStatsExporter powerStatsExporter,
- PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies,
- PowerStatsStore powerStatsStore, Clock clock) {
+ public BatteryUsageStatsProvider(@NonNull Context context,
+ @NonNull PowerAttributor powerAttributor,
+ @NonNull PowerProfile powerProfile, @NonNull CpuScalingPolicies cpuScalingPolicies,
+ @NonNull PowerStatsStore powerStatsStore, @NonNull Clock clock) {
mContext = context;
- mPowerStatsExporter = powerStatsExporter;
+ mPowerAttributor = powerAttributor;
mPowerStatsStore = powerStatsStore;
mPowerProfile = powerProfile;
mCpuScalingPolicies = cpuScalingPolicies;
mClock = clock;
+
+ mPowerStatsStore.addSectionReader(new BatteryUsageStatsSection.Reader());
}
private List<PowerCalculator> getPowerCalculators() {
@@ -72,55 +73,67 @@
// Power calculators are applied in the order of registration
mPowerCalculators.add(new BatteryChargeCalculator());
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_CPU)) {
mPowerCalculators.add(
new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
}
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
if (!BatteryStats.checkWifiOnly(mContext)) {
- if (!mPowerStatsExporterEnabled.get(
+ if (!mPowerAttributor.isPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) {
mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_PHONE)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_PHONE)) {
mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
}
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_WIFI)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_WIFI)) {
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) {
mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_SENSORS)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_SENSORS)) {
mPowerCalculators.add(new SensorPowerCalculator(
mContext.getSystemService(SensorManager.class)));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_GNSS)) {
mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_CAMERA)) {
mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_AUDIO)) {
mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_VIDEO)) {
mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_SCREEN)) {
mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
}
- if (!mPowerStatsExporterEnabled.get(
+ if (!mPowerAttributor.isPowerComponentSupported(
BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) {
mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
}
mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
- if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_ANY)) {
+ if (!mPowerAttributor.isPowerComponentSupported(
+ BatteryConsumer.POWER_COMPONENT_ANY)) {
mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
}
mPowerCalculators.add(new UserPowerCalculator());
@@ -196,7 +209,7 @@
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
&& stats.isProcessStateDataAvailable();
- final boolean includeVirtualUids = ((query.getFlags()
+ final boolean includeVirtualUids = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0);
final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
@@ -258,10 +271,8 @@
}
}
- if (mPowerStatsExporterEnabled.indexOfValue(true) >= 0) {
- mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder,
- monotonicStartTime, monotonicEndTime);
- }
+ mPowerAttributor.estimatePowerConsumption(batteryUsageStatsBuilder, stats.getHistory(),
+ monotonicStartTime, monotonicEndTime);
BatteryUsageStats batteryUsageStats = batteryUsageStatsBuilder.build();
if (includeProcessStateData) {
@@ -272,6 +283,7 @@
// STOPSHIP(b/229906525): remove verification before shipping
private static boolean sErrorReported;
+
private void verify(BatteryUsageStats stats) {
if (sErrorReported) {
return;
@@ -390,7 +402,7 @@
// while the "to" timestamp is *inclusive*.
boolean isInRange =
(query.getFromTimestamp() == 0 || minTime > query.getFromTimestamp())
- && (query.getToTimestamp() == 0 || maxTime <= query.getToTimestamp());
+ && (query.getToTimestamp() == 0 || maxTime <= query.getToTimestamp());
if (!isInRange) {
continue;
}
@@ -422,12 +434,4 @@
}
return builder.build();
}
-
- /**
- * Specify whether PowerStats based attribution is supported for the specified component.
- */
- public void setPowerStatsExporterEnabled(int powerComponentId, boolean enabled) {
- mPowerStatsExporterEnabled.put(powerComponentId, enabled);
- mPowerStatsExporter.setPowerComponentEnabled(powerComponentId, enabled);
- }
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java
index b95faac..af36524 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java
@@ -19,8 +19,11 @@
import android.os.BatteryUsageStats;
import android.util.IndentingPrintWriter;
+import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
class BatteryUsageStatsSection extends PowerStatsSpan.Section {
@@ -38,7 +41,7 @@
}
@Override
- void write(TypedXmlSerializer serializer) throws IOException {
+ public void write(TypedXmlSerializer serializer) throws IOException {
mBatteryUsageStats.writeXml(serializer);
}
@@ -46,4 +49,17 @@
public void dump(IndentingPrintWriter ipw) {
mBatteryUsageStats.dump(ipw, "");
}
+
+ static class Reader implements PowerStatsSpan.SectionReader {
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ @Override
+ public PowerStatsSpan.Section read(String sectionType, TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return new BatteryUsageStatsSection(BatteryUsageStats.createFromXml(parser));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
index 8a5085b..539c415 100644
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
@@ -28,13 +28,12 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
-import java.util.function.IntSupplier;
public class BluetoothPowerStatsCollector extends PowerStatsCollector {
private static final String TAG = "BluetoothPowerStatsCollector";
@@ -43,7 +42,7 @@
private static final long ENERGY_UNSPECIFIED = -1;
- interface BluetoothStatsRetriever {
+ public interface BluetoothStatsRetriever {
interface Callback {
void onBluetoothScanTime(int uid, long scanTimeMs);
}
@@ -54,29 +53,25 @@
BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback callback);
}
- interface Injector {
+ public interface Injector {
Handler getHandler();
Clock getClock();
PowerStatsUidResolver getUidResolver();
long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
PackageManager getPackageManager();
ConsumedEnergyRetriever getConsumedEnergyRetriever();
- IntSupplier getVoltageSupplier();
BluetoothStatsRetriever getBluetoothStatsRetriever();
}
private final Injector mInjector;
- private BluetoothPowerStatsLayout mLayout;
+ private com.android.server.power.stats.format.BluetoothPowerStatsLayout mLayout;
private boolean mIsInitialized;
private PowerStats mPowerStats;
private long[] mDeviceStats;
private BluetoothStatsRetriever mBluetoothStatsRetriever;
private ConsumedEnergyRetriever mConsumedEnergyRetriever;
- private IntSupplier mVoltageSupplier;
- private int[] mEnergyConsumerIds = new int[0];
- private long[] mLastConsumedEnergyUws;
- private int mLastVoltageMv;
+ private ConsumedEnergyHelper mConsumedEnergyHelper;
private long mLastRxTime;
private long mLastTxTime;
@@ -93,7 +88,7 @@
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
- BluetoothPowerStatsCollector(Injector injector) {
+ public BluetoothPowerStatsCollector(Injector injector) {
super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
BatteryConsumer.powerComponentIdToString(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH)),
@@ -122,21 +117,11 @@
return false;
}
- mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
- mVoltageSupplier = mInjector.getVoltageSupplier();
mBluetoothStatsRetriever = mInjector.getBluetoothStatsRetriever();
- mEnergyConsumerIds =
- mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH);
- mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
- Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
-
- mLayout = new BluetoothPowerStatsLayout();
- mLayout.addDeviceBluetoothControllerActivity();
- mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
- mLayout.addDeviceSectionUsageDuration();
- mLayout.addDeviceSectionPowerEstimate();
- mLayout.addUidTrafficStats();
- mLayout.addUidSectionPowerEstimate();
+ mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+ mConsumedEnergyHelper = new ConsumedEnergyHelper(mConsumedEnergyRetriever,
+ EnergyConsumerType.BLUETOOTH);
+ mLayout = new BluetoothPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount());
PersistableBundle extras = new PersistableBundle();
mLayout.toExtras(extras);
@@ -152,7 +137,7 @@
}
@Override
- protected PowerStats collectStats() {
+ public PowerStats collectStats() {
if (!ensureInitialized()) {
return null;
}
@@ -162,9 +147,7 @@
collectBluetoothActivityInfo();
collectBluetoothScanStats();
- if (mEnergyConsumerIds.length != 0) {
- collectEnergyConsumers();
- }
+ mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
return mPowerStats;
}
@@ -296,34 +279,6 @@
mLayout.setDeviceScanTime(mDeviceStats, totalScanTime);
}
- private void collectEnergyConsumers() {
- int voltageMv = mVoltageSupplier.getAsInt();
- if (voltageMv <= 0) {
- Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
- + " mV) when querying energy consumers");
- return;
- }
-
- int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
- mLastVoltageMv = voltageMv;
-
- long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
- if (energyUws == null) {
- return;
- }
-
- for (int i = energyUws.length - 1; i >= 0; i--) {
- long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
- ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
- if (energyDelta < 0) {
- // Likely, restart of powerstats HAL
- energyDelta = 0;
- }
- mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
- mLastConsumedEnergyUws[i] = energyUws[i];
- }
- }
-
@Override
protected void onUidRemoved(int uid) {
super.onUidRemoved(uid);
diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
index 8705bd5..0f70064 100644
--- a/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
@@ -19,12 +19,13 @@
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
+
public class CameraPowerStatsCollector extends EnergyConsumerPowerStatsCollector {
- CameraPowerStatsCollector(Injector injector) {
+ public CameraPowerStatsCollector(Injector injector) {
super(injector, BatteryConsumer.POWER_COMPONENT_CAMERA,
BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_CAMERA),
- EnergyConsumerType.CAMERA, /* energy consumer name */ null,
- new BinaryStatePowerStatsLayout());
+ EnergyConsumerType.CAMERA, new BinaryStatePowerStatsLayout());
}
}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index b5ef67b..128f14a 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -31,11 +31,10 @@
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.Locale;
-import java.util.function.IntSupplier;
/**
* Collects snapshots of power-related system statistics.
@@ -46,7 +45,6 @@
public class CpuPowerStatsCollector extends PowerStatsCollector {
private static final String TAG = "CpuPowerStatsCollector";
private static final long NANOS_PER_MILLIS = 1000000;
- private static final long ENERGY_UNSPECIFIED = -1;
private static final int DEFAULT_CPU_POWER_BRACKETS = 3;
private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2;
@@ -58,7 +56,6 @@
PowerProfile getPowerProfile();
KernelCpuStatsReader getKernelCpuStatsReader();
ConsumedEnergyRetriever getConsumedEnergyRetriever();
- IntSupplier getVoltageSupplier();
long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
default int getDefaultCpuPowerBrackets() {
@@ -76,8 +73,7 @@
private CpuScalingPolicies mCpuScalingPolicies;
private PowerProfile mPowerProfile;
private KernelCpuStatsReader mKernelCpuStatsReader;
- private ConsumedEnergyRetriever mConsumedEnergyRetriever;
- private IntSupplier mVoltageSupplier;
+ private ConsumedEnergyHelper mConsumedEnergyHelper;
private int mDefaultCpuPowerBrackets;
private int mDefaultCpuPowerBracketsPerEnergyConsumer;
private long[] mCpuTimeByScalingStep;
@@ -85,15 +81,12 @@
private long[] mTempUidStats;
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
private boolean mIsPerUidTimeInStateSupported;
- private int[] mCpuEnergyConsumerIds = new int[0];
private PowerStats.Descriptor mPowerStatsDescriptor;
// Reusable instance
private PowerStats mCpuPowerStats;
private CpuPowerStatsLayout mLayout;
private long mLastUpdateTimestampNanos;
private long mLastUpdateUptimeMillis;
- private int mLastVoltageMv;
- private long[] mLastConsumedEnergyUws;
CpuPowerStatsCollector(Injector injector) {
super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
@@ -115,31 +108,22 @@
mCpuScalingPolicies = mInjector.getCpuScalingPolicies();
mPowerProfile = mInjector.getPowerProfile();
mKernelCpuStatsReader = mInjector.getKernelCpuStatsReader();
- mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
- mVoltageSupplier = mInjector.getVoltageSupplier();
mDefaultCpuPowerBrackets = mInjector.getDefaultCpuPowerBrackets();
mDefaultCpuPowerBracketsPerEnergyConsumer =
mInjector.getDefaultCpuPowerBracketsPerEnergyConsumer();
mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.isSupportedFeature();
- mCpuEnergyConsumerIds =
- mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER);
- mLastConsumedEnergyUws = new long[mCpuEnergyConsumerIds.length];
- Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+
+ mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+ EnergyConsumerType.CPU_CLUSTER);
int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount();
mCpuTimeByScalingStep = new long[cpuScalingStepCount];
mTempCpuTimeByScalingStep = new long[cpuScalingStepCount];
int[] scalingStepToPowerBracketMap = initPowerBrackets();
- mLayout = new CpuPowerStatsLayout();
- mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount);
- mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length);
- mLayout.addDeviceSectionUsageDuration();
- mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length);
- mLayout.addDeviceSectionPowerEstimate();
- mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap);
- mLayout.addUidSectionPowerEstimate();
+ mLayout = new CpuPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount(),
+ mCpuScalingPolicies.getPolicies().length, scalingStepToPowerBracketMap);
PersistableBundle extras = new PersistableBundle();
mLayout.toExtras(extras);
@@ -158,16 +142,18 @@
private int[] initPowerBrackets() {
if (mPowerProfile.getCpuPowerBracketCount() != PowerProfile.POWER_BRACKETS_UNSPECIFIED) {
return initPowerBracketsFromPowerProfile();
- } else if (mCpuEnergyConsumerIds.length == 0 || mCpuEnergyConsumerIds.length == 1) {
+ } else if (mConsumedEnergyHelper.getEnergyConsumerCount() == 0
+ || mConsumedEnergyHelper.getEnergyConsumerCount() == 1) {
return initDefaultPowerBrackets(mDefaultCpuPowerBrackets);
- } else if (mCpuScalingPolicies.getPolicies().length == mCpuEnergyConsumerIds.length) {
+ } else if (mCpuScalingPolicies.getPolicies().length
+ == mConsumedEnergyHelper.getEnergyConsumerCount()) {
return initPowerBracketsByCluster(mDefaultCpuPowerBracketsPerEnergyConsumer);
} else {
Slog.i(TAG, "Assigning a single power brackets to each CPU_CLUSTER energy consumer."
+ " Number of CPU clusters ("
+ mCpuScalingPolicies.getPolicies().length
+ ") does not match the number of energy consumers ("
- + mCpuEnergyConsumerIds.length + "). "
+ + mConsumedEnergyHelper.getEnergyConsumerCount() + "). "
+ " Using default power bucket assignment.");
return initDefaultPowerBrackets(mDefaultCpuPowerBrackets);
}
@@ -368,41 +354,11 @@
}
mLayout.setUsageDuration(mCpuPowerStats.stats, uptimeDelta);
- if (mCpuEnergyConsumerIds.length != 0) {
- collectEnergyConsumers();
- }
+ mConsumedEnergyHelper.collectConsumedEnergy(mCpuPowerStats, mLayout);
return mCpuPowerStats;
}
- private void collectEnergyConsumers() {
- int voltageMv = mVoltageSupplier.getAsInt();
- if (voltageMv <= 0) {
- Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
- + " mV) when querying energy consumers");
- return;
- }
-
- int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
- mLastVoltageMv = voltageMv;
-
- long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mCpuEnergyConsumerIds);
- if (energyUws == null) {
- return;
- }
-
- for (int i = energyUws.length - 1; i >= 0; i--) {
- long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
- ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
- if (energyDelta < 0) {
- // Likely, restart of powerstats HAL
- energyDelta = 0;
- }
- mLayout.setConsumedEnergy(mCpuPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
- mLastConsumedEnergyUws[i] = energyUws[i];
- }
- }
-
@VisibleForNative
interface KernelCpuStatsCallback {
@Keep // Called from native
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
index 4bfe442..c1c7e12 100644
--- a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
@@ -19,6 +19,8 @@
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -29,7 +31,8 @@
private final EnergyConsumerPowerStatsCollector.Injector mInjector;
private List<EnergyConsumerPowerStatsCollector> mCollectors;
- CustomEnergyConsumerPowerStatsCollector(EnergyConsumerPowerStatsCollector.Injector injector) {
+ public CustomEnergyConsumerPowerStatsCollector(
+ EnergyConsumerPowerStatsCollector.Injector injector) {
super(injector.getHandler(), 0, injector.getUidResolver(), injector.getClock());
mInjector = injector;
}
@@ -46,7 +49,8 @@
for (int i = 0; i < energyConsumerIds.length; i++) {
String name = retriever.getEnergyConsumerName(energyConsumerIds[i]);
EnergyConsumerPowerStatsCollector collector = new EnergyConsumerPowerStatsCollector(
- mInjector, powerComponentId++, name, energyConsumerIds[i], sLayout);
+ mInjector, powerComponentId++, name, EnergyConsumerType.OTHER,
+ energyConsumerIds[i], sLayout);
collector.setEnabled(true);
collector.addConsumer(this::deliverStats);
mCollectors.add(collector);
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
index 79fbe8e..1d2e388 100644
--- a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
@@ -16,78 +16,57 @@
package com.android.server.power.stats;
-import android.hardware.power.stats.EnergyConsumerAttribution;
-import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.Handler;
import android.os.PersistableBundle;
-import android.util.Slog;
-import android.util.SparseLongArray;
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
-
-import java.util.function.IntSupplier;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
public class EnergyConsumerPowerStatsCollector extends PowerStatsCollector {
- private static final String TAG = "EnergyConsumerPowerStatsCollector";
- private static final long ENERGY_UNSPECIFIED = -1;
-
- interface Injector {
+ public interface Injector {
Handler getHandler();
Clock getClock();
PowerStatsUidResolver getUidResolver();
long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
ConsumedEnergyRetriever getConsumedEnergyRetriever();
- IntSupplier getVoltageSupplier();
}
+ private static final int UNSPECIFIED = -1;
+
private final Injector mInjector;
private final int mPowerComponentId;
private final String mPowerComponentName;
+ private final int mEnergyConsumerId;
private final int mEnergyConsumerType;
- private final String mEnergyConsumerName;
private final EnergyConsumerPowerStatsLayout mLayout;
private boolean mIsInitialized;
private PowerStats mPowerStats;
- private ConsumedEnergyRetriever mConsumedEnergyRetriever;
- private IntSupplier mVoltageSupplier;
- private int[] mEnergyConsumerIds;
- private long mLastConsumedEnergyUws = ENERGY_UNSPECIFIED;
- private SparseLongArray mLastConsumerEnergyPerUid = new SparseLongArray();
- private int mLastVoltageMv;
+ private ConsumedEnergyHelper mConsumedEnergyHelper;
private long mLastUpdateTimestamp;
- private boolean mFirstCollection = true;
EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId,
String powerComponentName, @EnergyConsumerType int energyConsumerType,
- String energyConsumerName, EnergyConsumerPowerStatsLayout statsLayout) {
- super(injector.getHandler(),
- injector.getPowerStatsCollectionThrottlePeriod(powerComponentName),
- injector.getUidResolver(), injector.getClock());
- mInjector = injector;
- mPowerComponentId = powerComponentId;
- mPowerComponentName = powerComponentName;
- mEnergyConsumerType = energyConsumerType;
- mEnergyConsumerName = energyConsumerName;
- mLayout = statsLayout;
+ EnergyConsumerPowerStatsLayout statsLayout) {
+ this(injector, powerComponentId, powerComponentName, energyConsumerType, UNSPECIFIED,
+ statsLayout);
}
EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId,
- String powerComponentName, int energyConsumerId,
- EnergyConsumerPowerStatsLayout statsLayout) {
+ String powerComponentName, @EnergyConsumerType int energyConsumerType,
+ int energyConsumerId, EnergyConsumerPowerStatsLayout statsLayout) {
super(injector.getHandler(),
injector.getPowerStatsCollectionThrottlePeriod(powerComponentName),
injector.getUidResolver(), injector.getClock());
mInjector = injector;
mPowerComponentId = powerComponentId;
mPowerComponentName = powerComponentName;
- mEnergyConsumerIds = new int[]{energyConsumerId};
- mEnergyConsumerType = EnergyConsumerType.OTHER;
- mEnergyConsumerName = null;
+ mEnergyConsumerId = energyConsumerId;
+ mEnergyConsumerType = energyConsumerType;
mLayout = statsLayout;
}
@@ -100,12 +79,14 @@
return false;
}
- mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
- mVoltageSupplier = mInjector.getVoltageSupplier();
- if (mEnergyConsumerIds == null) {
- mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(mEnergyConsumerType,
- mEnergyConsumerName);
+ if (mEnergyConsumerId != UNSPECIFIED) {
+ mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+ mEnergyConsumerId, true /* perUidAttribution */);
+ } else {
+ mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+ mEnergyConsumerType);
}
+
PersistableBundle extras = new PersistableBundle();
mLayout.toExtras(extras);
PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
@@ -124,85 +105,15 @@
return null;
}
- if (mEnergyConsumerIds.length == 0) {
- return null;
- }
-
- int voltageMv = mVoltageSupplier.getAsInt();
- int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
- if (averageVoltage <= 0) {
- Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
- + " mV) when querying energy consumers");
- return null;
- }
-
- mLastVoltageMv = voltageMv;
-
- EnergyConsumerResult[] energy =
- mConsumedEnergyRetriever.getConsumedEnergy(mEnergyConsumerIds);
- long consumedEnergy = 0;
- if (energy != null) {
- for (int i = energy.length - 1; i >= 0; i--) {
- if (energy[i].energyUWs != ENERGY_UNSPECIFIED) {
- consumedEnergy += energy[i].energyUWs;
- }
- }
- }
-
- long energyDelta = mLastConsumedEnergyUws != ENERGY_UNSPECIFIED
- ? consumedEnergy - mLastConsumedEnergyUws : 0;
- mLastConsumedEnergyUws = consumedEnergy;
- if (energyDelta < 0) {
- // Likely, restart of powerstats HAL
- energyDelta = 0;
- }
-
- if (energyDelta == 0 && !mFirstCollection) {
- return null;
- }
-
- mLayout.setConsumedEnergy(mPowerStats.stats, 0, uJtoUc(energyDelta, averageVoltage));
-
mPowerStats.uidStats.clear();
- if (energy != null) {
- for (int i = energy.length - 1; i >= 0; i--) {
- EnergyConsumerAttribution[] perUid = energy[i].attribution;
- if (perUid == null) {
- continue;
- }
- for (EnergyConsumerAttribution attribution : perUid) {
- int uid = mUidResolver.mapUid(attribution.uid);
- long lastEnergy = mLastConsumerEnergyPerUid.get(uid, ENERGY_UNSPECIFIED);
- mLastConsumerEnergyPerUid.put(uid, attribution.energyUWs);
- if (lastEnergy == ENERGY_UNSPECIFIED) {
- continue;
- }
- long deltaEnergy = attribution.energyUWs - lastEnergy;
- if (deltaEnergy <= 0) {
- continue;
- }
- long[] uidStats = mPowerStats.uidStats.get(uid);
- if (uidStats == null) {
- uidStats = new long[mLayout.getUidStatsArrayLength()];
- mPowerStats.uidStats.put(uid, uidStats);
- }
-
- mLayout.setUidConsumedEnergy(uidStats, 0,
- mLayout.getUidConsumedEnergy(uidStats, 0)
- + uJtoUc(deltaEnergy, averageVoltage));
- }
- }
+ if (!mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout)) {
+ return null;
}
+
long timestamp = mClock.elapsedRealtime();
mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
mLastUpdateTimestamp = timestamp;
- mFirstCollection = false;
return mPowerStats;
}
-
- @Override
- protected void onUidRemoved(int uid) {
- mLastConsumerEnergyPerUid.delete(uid);
- }
}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
index 168a874..c1b4c31 100644
--- a/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
@@ -19,12 +19,13 @@
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
+import com.android.server.power.stats.format.GnssPowerStatsLayout;
+
public class GnssPowerStatsCollector extends EnergyConsumerPowerStatsCollector {
- GnssPowerStatsCollector(Injector injector) {
+ public GnssPowerStatsCollector(Injector injector) {
super(injector, BatteryConsumer.POWER_COMPONENT_GNSS,
BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_GNSS),
- EnergyConsumerType.GNSS, /* energy consumer name */ null,
- new GnssPowerStatsLayout());
+ EnergyConsumerType.GNSS, new GnssPowerStatsLayout());
}
}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
index c88e1b0..cbd6fab 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -35,12 +35,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
-import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
@@ -56,8 +56,6 @@
private static final long MODEM_ACTIVITY_REQUEST_TIMEOUT = 20000;
- private static final long ENERGY_UNSPECIFIED = -1;
-
@VisibleForTesting
@AccessNetworkConstants.RadioAccessNetworkType
static final int[] NETWORK_TYPES = {
@@ -77,14 +75,13 @@
long elapsedRealtimeMs, long uptimeMs);
}
- interface Injector {
+ public interface Injector {
Handler getHandler();
Clock getClock();
PowerStatsUidResolver getUidResolver();
long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
PackageManager getPackageManager();
ConsumedEnergyRetriever getConsumedEnergyRetriever();
- IntSupplier getVoltageSupplier();
Supplier<NetworkStats> getMobileNetworkStatsSupplier();
TelephonyManager getTelephonyManager();
LongSupplier getCallDurationSupplier();
@@ -103,18 +100,14 @@
private LongSupplier mCallDurationSupplier;
private LongSupplier mScanDurationSupplier;
private volatile Supplier<NetworkStats> mNetworkStatsSupplier;
- private ConsumedEnergyRetriever mConsumedEnergyRetriever;
- private IntSupplier mVoltageSupplier;
- private int[] mEnergyConsumerIds = new int[0];
+ private ConsumedEnergyHelper mConsumedEnergyHelper;
private long mLastUpdateTimestampMillis;
private ModemActivityInfo mLastModemActivityInfo;
private NetworkStats mLastNetworkStats;
- private long[] mLastConsumedEnergyUws;
- private int mLastVoltageMv;
private long mLastCallDuration;
private long mLastScanDuration;
- MobileRadioPowerStatsCollector(Injector injector, Observer observer) {
+ public MobileRadioPowerStatsCollector(Injector injector, Observer observer) {
super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
BatteryConsumer.powerComponentIdToString(
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)),
@@ -144,34 +137,22 @@
return false;
}
- mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
- mVoltageSupplier = mInjector.getVoltageSupplier();
-
mTelephonyManager = mInjector.getTelephonyManager();
mNetworkStatsSupplier = mInjector.getMobileNetworkStatsSupplier();
mCallDurationSupplier = mInjector.getCallDurationSupplier();
mScanDurationSupplier = mInjector.getPhoneSignalScanDurationSupplier();
- mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
+ mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
EnergyConsumerType.MOBILE_RADIO);
- mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
- Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
- mLayout = new MobileRadioPowerStatsLayout();
- mLayout.addDeviceMobileActivity();
- mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
- mLayout.addStateStats();
- mLayout.addUidNetworkStats();
- mLayout.addDeviceSectionUsageDuration();
- mLayout.addDeviceSectionPowerEstimate();
- mLayout.addUidSectionPowerEstimate();
+ mLayout = new MobileRadioPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount());
SparseArray<String> stateLabels = new SparseArray<>();
for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
? ServiceState.FREQUENCY_RANGE_COUNT : 1;
for (int freq = 0; freq < freqCount; freq++) {
- int stateKey = makeStateKey(rat, freq);
+ int stateKey = MobileRadioPowerStatsLayout.makeStateKey(rat, freq);
StringBuilder sb = new StringBuilder();
if (rat != BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER) {
sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
@@ -200,7 +181,7 @@
}
@Override
- protected PowerStats collectStats() {
+ public PowerStats collectStats() {
if (!ensureInitialized()) {
return null;
}
@@ -210,9 +191,8 @@
ModemActivityInfo modemActivityDelta = collectModemActivityInfo();
List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas = collectNetworkStats();
- if (mEnergyConsumerIds.length != 0) {
- collectEnergyConsumers();
- }
+
+ mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
if (mPowerStats.durationMs == 0) {
setTimestamp(mClock.elapsedRealtime());
@@ -346,65 +326,8 @@
return delta;
}
- private void collectEnergyConsumers() {
- int voltageMv = mVoltageSupplier.getAsInt();
- if (voltageMv <= 0) {
- Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
- + " mV) when querying energy consumers");
- return;
- }
-
- int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
- mLastVoltageMv = voltageMv;
-
- long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
- if (energyUws == null) {
- return;
- }
-
- for (int i = energyUws.length - 1; i >= 0; i--) {
- long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
- ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
- if (energyDelta < 0) {
- // Likely, restart of powerstats HAL
- energyDelta = 0;
- }
- mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
- mLastConsumedEnergyUws[i] = energyUws[i];
- }
- }
-
- static int makeStateKey(int rat, int freqRange) {
- if (rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR) {
- return rat | (freqRange << 8);
- } else {
- return rat;
- }
- }
-
private void setTimestamp(long timestamp) {
mPowerStats.durationMs = Math.max(timestamp - mLastUpdateTimestampMillis, 0);
mLastUpdateTimestampMillis = timestamp;
}
-
- @BatteryStats.RadioAccessTechnology
- static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
- @AccessNetworkConstants.RadioAccessNetworkType int networkType) {
- switch (networkType) {
- case AccessNetworkConstants.AccessNetworkType.NGRAN:
- return BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
- case AccessNetworkConstants.AccessNetworkType.EUTRAN:
- return BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE;
- case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.IWLAN:
- return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
- default:
- Slog.w(TAG,
- "Unhandled RadioAccessNetworkType (" + networkType + "), mapping to OTHER");
- return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
- }
- }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerAttributor.java b/services/core/java/com/android/server/power/stats/PowerAttributor.java
new file mode 100644
index 0000000..d1f8564
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerAttributor.java
@@ -0,0 +1,59 @@
+/*
+ * 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.server.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.os.BatteryStatsHistory;
+
+public interface PowerAttributor {
+
+ /**
+ * Returns true if the specified power component can be handled by this PowerAttributor
+ */
+ boolean isPowerComponentSupported(@BatteryConsumer.PowerComponentId int powerComponentId);
+
+ /**
+ * Performs the power attribution calculations and returns the results by populating the
+ * supplied BatteryUsageStats.Builder
+ */
+ void estimatePowerConsumption(BatteryUsageStats.Builder batteryUsageStatsBuilder,
+ BatteryStatsHistory batteryHistory, long monotonicStartTime, long monotonicEndTime);
+
+ /**
+ * Computes estimated power consumption attribution for the specified time range and stores
+ * it in PowerStatsStore for potential accumulation.
+ *
+ * Returns the monotonic timestamp of the last processed history item.
+ */
+ long storeEstimatedPowerConsumption(BatteryStatsHistory batteryStatsHistory, long startTime,
+ long endTimeMs);
+
+ /**
+ * Returns the monotonic timestamp of the last processed history item, stored in
+ * PowerStatsStore.
+ */
+ long getLastSavedEstimatesPowerConsumptionTimestamp();
+
+ /**
+ * Performs the power attribution calculation and prints the results.
+ */
+ void dumpEstimatedPowerConsumption(IndentingPrintWriter ipw,
+ BatteryStatsHistory batteryStatsHistory, long startTime, long endTime);
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index f5b0005..e5b990e 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.ConditionVariable;
@@ -26,13 +27,16 @@
import android.power.PowerStatsInternal;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.PowerStatsLayout;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -41,6 +45,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
+import java.util.function.IntSupplier;
/**
* Collects snapshots of power-related system statistics.
@@ -53,6 +58,8 @@
private static final String TAG = "PowerStatsCollector";
private static final int MILLIVOLTS_PER_VOLT = 1000;
private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000;
+ private static final long ENERGY_UNSPECIFIED = -1;
+
private final Handler mHandler;
protected final PowerStatsUidResolver mUidResolver;
protected final Clock mClock;
@@ -235,46 +242,31 @@
return (deltaEnergyUj * MILLIVOLTS_PER_VOLT + (avgVoltageMv / 2)) / avgVoltageMv;
}
- interface ConsumedEnergyRetriever {
+ public interface ConsumedEnergyRetriever {
+
@NonNull
- int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType, String name);
+ int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType);
String getEnergyConsumerName(int energyConsumerId);
@Nullable
EnergyConsumerResult[] getConsumedEnergy(int[] energyConsumerIds);
- @Nullable
- default long[] getConsumedEnergyUws(int[] energyConsumerIds) {
- EnergyConsumerResult[] results = getConsumedEnergy(energyConsumerIds);
- if (results == null) {
- return null;
- }
-
- long[] energy = new long[energyConsumerIds.length];
- for (int i = 0; i < energyConsumerIds.length; i++) {
- int id = energyConsumerIds[i];
- for (EnergyConsumerResult result : results) {
- if (result.id == id) {
- energy[i] = result.energyUWs;
- break;
- }
- }
- }
- return energy;
- }
-
- default int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType) {
- return getEnergyConsumerIds(energyConsumerType, null);
- }
+ /**
+ * Returns the last known battery/charger voltage in milli-volts.
+ */
+ int getVoltageMv();
}
static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever {
private final PowerStatsInternal mPowerStatsInternal;
+ private final IntSupplier mVoltageSupplier;
private EnergyConsumer[] mEnergyConsumers;
- ConsumedEnergyRetrieverImpl(PowerStatsInternal powerStatsInternal) {
+ ConsumedEnergyRetrieverImpl(PowerStatsInternal powerStatsInternal,
+ IntSupplier voltageSupplier) {
mPowerStatsInternal = powerStatsInternal;
+ mVoltageSupplier = voltageSupplier;
}
private void ensureEnergyConsumers() {
@@ -293,8 +285,9 @@
}
}
+ @NonNull
@Override
- public int[] getEnergyConsumerIds(int energyConsumerType, String name) {
+ public int[] getEnergyConsumerIds(int energyConsumerType) {
ensureEnergyConsumers();
if (mEnergyConsumers.length == 0) {
@@ -303,8 +296,7 @@
List<EnergyConsumer> energyConsumers = new ArrayList<>();
for (EnergyConsumer energyConsumer : mEnergyConsumers) {
- if (energyConsumer.type == energyConsumerType
- && (name == null || name.equals(energyConsumer.name))) {
+ if (energyConsumer.type == energyConsumerType) {
energyConsumers.add(energyConsumer);
}
}
@@ -335,6 +327,11 @@
}
@Override
+ public int getVoltageMv() {
+ return mVoltageSupplier.getAsInt();
+ }
+
+ @Override
public String getEnergyConsumerName(int energyConsumerId) {
ensureEnergyConsumers();
@@ -368,4 +365,149 @@
return sb.toString();
}
}
+
+ class ConsumedEnergyHelper implements PowerStatsUidResolver.Listener {
+ private final ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ private final @EnergyConsumerType int mEnergyConsumerType;
+ private final boolean mPerUidAttributionSupported;
+
+ private boolean mIsInitialized;
+ private boolean mFirstCollection = true;
+ private int[] mEnergyConsumerIds;
+ private long[] mLastConsumedEnergyUws;
+ private final SparseLongArray mLastConsumerEnergyPerUid;
+ private int mLastVoltageMv;
+
+ ConsumedEnergyHelper(ConsumedEnergyRetriever consumedEnergyRetriever,
+ @EnergyConsumerType int energyConsumerType) {
+ mConsumedEnergyRetriever = consumedEnergyRetriever;
+ mEnergyConsumerType = energyConsumerType;
+ mPerUidAttributionSupported = false;
+ mLastConsumerEnergyPerUid = null;
+ }
+
+ ConsumedEnergyHelper(ConsumedEnergyRetriever consumedEnergyRetriever,
+ int energyConsumerId, boolean perUidAttributionSupported) {
+ mConsumedEnergyRetriever = consumedEnergyRetriever;
+ mEnergyConsumerType = EnergyConsumerType.OTHER;
+ mEnergyConsumerIds = new int[]{energyConsumerId};
+ mPerUidAttributionSupported = perUidAttributionSupported;
+ mLastConsumerEnergyPerUid = mPerUidAttributionSupported ? new SparseLongArray() : null;
+ }
+
+ private void ensureInitialized() {
+ if (!mIsInitialized) {
+ if (mEnergyConsumerIds == null) {
+ mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
+ mEnergyConsumerType);
+ }
+ mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
+ Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+ mUidResolver.addListener(this);
+ mIsInitialized = true;
+ }
+ }
+
+ int getEnergyConsumerCount() {
+ ensureInitialized();
+ return mEnergyConsumerIds.length;
+ }
+
+ boolean collectConsumedEnergy(PowerStats powerStats, PowerStatsLayout layout) {
+ ensureInitialized();
+
+ if (mEnergyConsumerIds.length == 0) {
+ return false;
+ }
+
+ int voltageMv = mConsumedEnergyRetriever.getVoltageMv();
+ int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+ if (averageVoltage <= 0) {
+ Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+ + " mV) when querying energy consumers");
+ return false;
+ }
+
+ mLastVoltageMv = voltageMv;
+
+ EnergyConsumerResult[] energy =
+ mConsumedEnergyRetriever.getConsumedEnergy(mEnergyConsumerIds);
+ if (energy == null) {
+ return false;
+ }
+
+ for (int i = 0; i < mEnergyConsumerIds.length; i++) {
+ populatePowerStats(powerStats, layout, energy, i, averageVoltage);
+ }
+ mFirstCollection = false;
+ return true;
+ }
+
+ private void populatePowerStats(PowerStats powerStats, PowerStatsLayout layout,
+ @NonNull EnergyConsumerResult[] energy, int energyConsumerIndex,
+ int averageVoltage) {
+ long consumedEnergy = energy[energyConsumerIndex].energyUWs;
+ long energyDelta = mLastConsumedEnergyUws[energyConsumerIndex] != ENERGY_UNSPECIFIED
+ ? consumedEnergy - mLastConsumedEnergyUws[energyConsumerIndex] : 0;
+ mLastConsumedEnergyUws[energyConsumerIndex] = consumedEnergy;
+ if (energyDelta < 0) {
+ // Likely, restart of powerstats HAL
+ energyDelta = 0;
+ }
+
+ if (energyDelta == 0 && !mFirstCollection) {
+ return;
+ }
+
+ layout.setConsumedEnergy(powerStats.stats, energyConsumerIndex,
+ uJtoUc(energyDelta, averageVoltage));
+
+ if (!mPerUidAttributionSupported) {
+ return;
+ }
+
+ EnergyConsumerAttribution[] perUid = energy[energyConsumerIndex].attribution;
+ if (perUid == null) {
+ return;
+ }
+
+ for (EnergyConsumerAttribution attribution : perUid) {
+ int uid = mUidResolver.mapUid(attribution.uid);
+ long lastEnergy = mLastConsumerEnergyPerUid.get(uid, ENERGY_UNSPECIFIED);
+ mLastConsumerEnergyPerUid.put(uid, attribution.energyUWs);
+ if (lastEnergy == ENERGY_UNSPECIFIED) {
+ continue;
+ }
+ long deltaEnergy = attribution.energyUWs - lastEnergy;
+ if (deltaEnergy <= 0) {
+ continue;
+ }
+
+ long[] uidStats = powerStats.uidStats.get(uid);
+ if (uidStats == null) {
+ uidStats = new long[layout.getUidStatsArrayLength()];
+ powerStats.uidStats.put(uid, uidStats);
+ }
+
+ layout.setUidConsumedEnergy(uidStats, energyConsumerIndex,
+ layout.getUidConsumedEnergy(uidStats, energyConsumerIndex)
+ + uJtoUc(deltaEnergy, averageVoltage));
+ }
+ }
+
+ @Override
+ public void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ if (mLastConsumerEnergyPerUid != null) {
+ mHandler.post(() -> mLastConsumerEnergyPerUid.delete(isolatedUid));
+ }
+ }
+
+ @Override
+ public void onIsolatedUidAdded(int isolatedUid, int parentUid) {
+ }
+
+ @Override
+ public void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
index abe4c0c..38ca087 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -23,6 +23,7 @@
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.Clock;
import com.android.internal.os.MonotonicClock;
@@ -51,7 +52,8 @@
private final Handler mHandler;
private final Runnable mPowerStatsCollector;
private final Supplier<Long> mEarliestAvailableBatteryHistoryTimeMs;
- private final PowerStatsAggregator mPowerStatsAggregator;
+ private final BatteryStatsHistory mBatteryStatsHistory;
+ private final PowerAttributor mPowerAttributor;
private long mLastSavedSpanEndMonotonicTime;
/**
@@ -66,12 +68,13 @@
}
public PowerStatsScheduler(Runnable powerStatsCollector,
- PowerStatsAggregator powerStatsAggregator,
+ BatteryStatsHistory batteryStatsHistory, PowerAttributor powerAttributor,
@DurationMillisLong long aggregatedPowerStatsSpanDuration,
@DurationMillisLong long powerStatsAggregationPeriod, PowerStatsStore powerStatsStore,
AlarmScheduler alarmScheduler, Clock clock, MonotonicClock monotonicClock,
Supplier<Long> earliestAvailableBatteryHistoryTimeMs, Handler handler) {
- mPowerStatsAggregator = powerStatsAggregator;
+ mBatteryStatsHistory = batteryStatsHistory;
+ mPowerAttributor = powerAttributor;
mAggregatedPowerStatsSpanDuration = aggregatedPowerStatsSpanDuration;
mPowerStatsAggregationPeriod = powerStatsAggregationPeriod;
mPowerStatsStore = powerStatsStore;
@@ -123,12 +126,8 @@
long endTimeMs = alignToWallClock(startTime + mAggregatedPowerStatsSpanDuration,
mAggregatedPowerStatsSpanDuration, currentMonotonicTime, currentTimeMillis);
while (endTimeMs <= currentMonotonicTime) {
- mPowerStatsAggregator.aggregatePowerStats(startTime, endTimeMs,
- stats -> {
- storeAggregatedPowerStats(stats);
- mLastSavedSpanEndMonotonicTime = stats.getStartTime() + stats.getDuration();
- });
-
+ mLastSavedSpanEndMonotonicTime = mPowerAttributor.storeEstimatedPowerConsumption(
+ mBatteryStatsHistory, startTime, endTimeMs);
startTime = endTimeMs;
endTimeMs += mAggregatedPowerStatsSpanDuration;
}
@@ -153,15 +152,8 @@
mPowerStatsStore.dump(ipw);
// Aggregate the remainder of power stats and dump the results without storing them yet.
long powerStoreEndMonotonicTime = getLastSavedSpanEndMonotonicTime();
- mPowerStatsAggregator.aggregatePowerStats(powerStoreEndMonotonicTime,
- MonotonicClock.UNDEFINED,
- stats -> {
- // Create a PowerStatsSpan for consistency of the textual output
- PowerStatsSpan span = PowerStatsStore.createPowerStatsSpan(stats);
- if (span != null) {
- span.dump(ipw);
- }
- });
+ mPowerAttributor.dumpEstimatedPowerConsumption(ipw, mBatteryStatsHistory,
+ powerStoreEndMonotonicTime, MonotonicClock.UNDEFINED);
});
awaitCompletion();
@@ -223,28 +215,13 @@
}
private long getLastSavedSpanEndMonotonicTime() {
- if (mLastSavedSpanEndMonotonicTime != 0) {
- return mLastSavedSpanEndMonotonicTime;
- }
-
- mLastSavedSpanEndMonotonicTime = -1;
- for (PowerStatsSpan.Metadata metadata : mPowerStatsStore.getTableOfContents()) {
- if (metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
- for (PowerStatsSpan.TimeFrame timeFrame : metadata.getTimeFrames()) {
- long endMonotonicTime = timeFrame.startMonotonicTime + timeFrame.duration;
- if (endMonotonicTime > mLastSavedSpanEndMonotonicTime) {
- mLastSavedSpanEndMonotonicTime = endMonotonicTime;
- }
- }
- }
+ if (mLastSavedSpanEndMonotonicTime == 0) {
+ mLastSavedSpanEndMonotonicTime =
+ mPowerAttributor.getLastSavedEstimatesPowerConsumptionTimestamp();
}
return mLastSavedSpanEndMonotonicTime;
}
- private void storeAggregatedPowerStats(AggregatedPowerStats stats) {
- mPowerStatsStore.storeAggregatedPowerStats(stats);
- }
-
private void awaitCompletion() {
ConditionVariable done = new ConditionVariable();
mHandler.post(done::open);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java
index 4df919d..fc0611f 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java
@@ -44,6 +44,7 @@
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -72,7 +73,7 @@
private static final DateTimeFormatter DATE_FORMAT =
DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
- static class TimeFrame {
+ public static class TimeFrame {
public final long startMonotonicTime;
@CurrentTimeMillisLong
public final long startTime;
@@ -119,7 +120,7 @@
}
}
- static class Metadata {
+ public static class Metadata {
static final Comparator<Metadata> COMPARATOR = Comparator.comparing(Metadata::getId);
private final long mId;
@@ -262,7 +263,7 @@
public abstract static class Section {
private final String mType;
- Section(String type) {
+ protected Section(String type) {
mType = type;
}
@@ -274,7 +275,10 @@
return mType;
}
- abstract void write(TypedXmlSerializer serializer) throws IOException;
+ /**
+ * Adds the contents of this section to the XML doc.
+ */
+ public abstract void write(TypedXmlSerializer serializer) throws IOException;
/**
* Prints the section type.
@@ -290,6 +294,11 @@
*/
public interface SectionReader {
/**
+ * Returns the unique type of content handled by this reader.
+ */
+ String getType();
+
+ /**
* Reads the contents of the section using the parser. The type of the object
* read and the corresponding XML format are determined by the section type.
*/
@@ -316,12 +325,18 @@
return mMetadata.mId;
}
- void addTimeFrame(long monotonicTime, @CurrentTimeMillisLong long wallClockTime,
+ /**
+ * Adds a time frame covered by this PowerStats span
+ */
+ public void addTimeFrame(long monotonicTime, @CurrentTimeMillisLong long wallClockTime,
@DurationMillisLong long duration) {
mMetadata.mTimeFrames.add(new TimeFrame(monotonicTime, wallClockTime, duration));
}
- void addSection(Section section) {
+ /**
+ * Adds the supplied section to the span.
+ */
+ public void addSection(Section section) {
mMetadata.addSection(section.getType());
mSections.add(section);
}
@@ -354,7 +369,7 @@
@Nullable
static PowerStatsSpan read(InputStream in, TypedXmlPullParser parser,
- SectionReader sectionReader, String... sectionTypes)
+ Map<String, SectionReader> sectionReaders, String... sectionTypes)
throws IOException, XmlPullParserException {
Set<String> neededSections = Sets.newArraySet(sectionTypes);
boolean selectSections = !neededSections.isEmpty();
@@ -386,7 +401,11 @@
if (tag.equals(XML_TAG_SECTION)) {
String sectionType = parser.getAttributeValue(null, XML_ATTR_SECTION_TYPE);
if (!selectSections || neededSections.contains(sectionType)) {
- Section section = sectionReader.read(sectionType, parser);
+ Section section = null;
+ SectionReader sectionReader = sectionReaders.get(sectionType);
+ if (sectionReader != null) {
+ section = sectionReader.read(sectionType, parser);
+ }
if (section == null) {
if (selectSections) {
throw new XmlPullParserException(
@@ -396,11 +415,11 @@
@Override
public void dump(IndentingPrintWriter ipw) {
ipw.println("Unsupported PowerStatsStore section type: "
- + sectionType);
+ + sectionType);
}
@Override
- void write(TypedXmlSerializer serializer) {
+ public void write(TypedXmlSerializer serializer) {
}
};
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsStore.java b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
index 7bcdc71..a875c30 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsStore.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
@@ -42,6 +42,7 @@
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -66,28 +67,31 @@
private FileLock mJvmLock;
private final long mMaxStorageBytes;
private final Handler mHandler;
- private final PowerStatsSpan.SectionReader mSectionReader;
+ private final Map<String, PowerStatsSpan.SectionReader> mSectionReaders = new HashMap<>();
private volatile List<PowerStatsSpan.Metadata> mTableOfContents;
- public PowerStatsStore(@NonNull File systemDir, Handler handler,
- AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
- this(systemDir, MAX_POWER_STATS_SPAN_STORAGE_BYTES, handler,
- new DefaultSectionReader(aggregatedPowerStatsConfig));
+ public PowerStatsStore(@NonNull File systemDir, Handler handler) {
+ this(systemDir, MAX_POWER_STATS_SPAN_STORAGE_BYTES, handler);
}
@VisibleForTesting
- public PowerStatsStore(@NonNull File systemDir, long maxStorageBytes, Handler handler,
- @NonNull PowerStatsSpan.SectionReader sectionReader) {
+ public PowerStatsStore(@NonNull File systemDir, long maxStorageBytes, Handler handler) {
mSystemDir = systemDir;
mStoreDir = new File(systemDir, POWER_STATS_DIR);
mLockFile = new File(mStoreDir, DIR_LOCK_FILENAME);
mHandler = handler;
mMaxStorageBytes = maxStorageBytes;
- mSectionReader = sectionReader;
mHandler.post(this::maybeClearLegacyStore);
}
/**
+ * Registers a Reader for a section type, which is determined by `sectionReader.getType()`
+ */
+ public void addSectionReader(PowerStatsSpan.SectionReader sectionReader) {
+ mSectionReaders.put(sectionReader.getType(), sectionReader);
+ }
+
+ /**
* Returns the metadata for all {@link PowerStatsSpan}'s contained in the store.
*/
@NonNull
@@ -169,7 +173,7 @@
try {
File file = makePowerStatsSpanFilename(id);
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
- return PowerStatsSpan.read(inputStream, parser, mSectionReader, sectionTypes);
+ return PowerStatsSpan.read(inputStream, parser, mSectionReaders, sectionTypes);
} catch (IOException | XmlPullParserException e) {
Slog.wtf(TAG, "Cannot read PowerStatsSpan file: " + file, e);
}
@@ -179,41 +183,6 @@
return null;
}
- void storeAggregatedPowerStats(AggregatedPowerStats stats) {
- PowerStatsSpan span = createPowerStatsSpan(stats);
- if (span == null) {
- return;
- }
- storePowerStatsSpan(span);
- }
-
- static PowerStatsSpan createPowerStatsSpan(AggregatedPowerStats stats) {
- List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
- if (clockUpdates.isEmpty()) {
- Slog.w(TAG, "No clock updates in aggregated power stats " + stats);
- return null;
- }
-
- long monotonicTime = clockUpdates.get(0).monotonicTime;
- long durationSum = 0;
- PowerStatsSpan span = new PowerStatsSpan(monotonicTime);
- for (int i = 0; i < clockUpdates.size(); i++) {
- AggregatedPowerStats.ClockUpdate clockUpdate = clockUpdates.get(i);
- long duration;
- if (i == clockUpdates.size() - 1) {
- duration = stats.getDuration() - durationSum;
- } else {
- duration = clockUpdate.monotonicTime - monotonicTime;
- }
- span.addTimeFrame(clockUpdate.monotonicTime, clockUpdate.currentTime, duration);
- monotonicTime = clockUpdate.monotonicTime;
- durationSum += duration;
- }
-
- span.addSection(new AggregatedPowerStatsSection(stats));
- return span;
- }
-
/**
* Stores a {@link PowerStatsSpan} containing a single section for the supplied
* battery usage stats.
@@ -344,28 +313,4 @@
}
ipw.decreaseIndent();
}
-
- private static class DefaultSectionReader implements PowerStatsSpan.SectionReader {
- private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
-
- DefaultSectionReader(AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
- mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
- }
-
- @Override
- public PowerStatsSpan.Section read(String sectionType, TypedXmlPullParser parser)
- throws IOException, XmlPullParserException {
- switch (sectionType) {
- case AggregatedPowerStatsSection.TYPE:
- return new AggregatedPowerStatsSection(
- AggregatedPowerStats.createFromXml(parser,
- mAggregatedPowerStatsConfig));
- case BatteryUsageStatsSection.TYPE:
- return new BatteryUsageStatsSection(
- BatteryUsageStats.createFromXml(parser));
- default:
- return null;
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
index 291f289..8371e66 100644
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
@@ -21,19 +21,16 @@
import android.os.BatteryStats;
import android.os.Handler;
import android.os.PersistableBundle;
-import android.util.Slog;
import android.util.SparseLongArray;
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
-
-import java.util.Arrays;
-import java.util.function.IntSupplier;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
public class ScreenPowerStatsCollector extends PowerStatsCollector {
private static final String TAG = "ScreenPowerStatsCollector";
- interface ScreenUsageTimeRetriever {
+ public interface ScreenUsageTimeRetriever {
interface Callback {
void onUidTopActivityTime(int uid, long topActivityTimeMs);
}
@@ -45,30 +42,23 @@
long getScreenDozeTimeMs(int display);
}
- interface Injector {
+ public interface Injector {
Handler getHandler();
Clock getClock();
PowerStatsUidResolver getUidResolver();
long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
ConsumedEnergyRetriever getConsumedEnergyRetriever();
- IntSupplier getVoltageSupplier();
ScreenUsageTimeRetriever getScreenUsageTimeRetriever();
int getDisplayCount();
}
- private static final long ENERGY_UNSPECIFIED = -1;
-
private final Injector mInjector;
private boolean mIsInitialized;
private ScreenPowerStatsLayout mLayout;
private int mDisplayCount;
private PowerStats mPowerStats;
- private ConsumedEnergyRetriever mConsumedEnergyRetriever;
- private IntSupplier mVoltageSupplier;
+ private ConsumedEnergyHelper mConsumedEnergyHelper;
private ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
- private int[] mEnergyConsumerIds = new int[0];
- private long[] mLastConsumedEnergyUws;
- private int mLastVoltageMv;
private boolean mFirstSample = true;
private long[] mLastScreenOnTime;
private long[][] mLastBrightnessLevelTime;
@@ -76,7 +66,7 @@
private final SparseLongArray mLastTopActivityTime = new SparseLongArray();
private long mLastCollectionTime;
- ScreenPowerStatsCollector(Injector injector) {
+ public ScreenPowerStatsCollector(Injector injector) {
super(injector.getHandler(),
injector.getPowerStatsCollectionThrottlePeriod(
BatteryConsumer.powerComponentIdToString(
@@ -95,21 +85,12 @@
}
mDisplayCount = mInjector.getDisplayCount();
- mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
- mVoltageSupplier = mInjector.getVoltageSupplier();
mScreenUsageTimeRetriever = mInjector.getScreenUsageTimeRetriever();
- mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
- EnergyConsumerType.DISPLAY);
- mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
- Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
- mLayout = new ScreenPowerStatsLayout();
- mLayout.addDeviceScreenUsageDurationSection(mInjector.getDisplayCount());
- mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
- mLayout.addDeviceSectionUsageDuration();
- mLayout.addDeviceSectionPowerEstimate();
- mLayout.addUidTopActivitiyDuration();
- mLayout.addUidSectionPowerEstimate();
+ mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+ EnergyConsumerType.DISPLAY);
+ mLayout = new ScreenPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount(),
+ mInjector.getDisplayCount());
PersistableBundle extras = new PersistableBundle();
mLayout.toExtras(extras);
@@ -129,14 +110,12 @@
}
@Override
- protected PowerStats collectStats() {
+ public PowerStats collectStats() {
if (!ensureInitialized()) {
return null;
}
- if (mEnergyConsumerIds.length != 0) {
- collectEnergyConsumers();
- }
+ mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
for (int display = 0; display < mDisplayCount; display++) {
long screenOnTimeMs = mScreenUsageTimeRetriever.getScreenOnTimeMs(display);
@@ -192,34 +171,6 @@
return mPowerStats;
}
- private void collectEnergyConsumers() {
- int voltageMv = mVoltageSupplier.getAsInt();
- if (voltageMv <= 0) {
- Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
- + " mV) when querying energy consumers");
- return;
- }
-
- int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
- mLastVoltageMv = voltageMv;
-
- long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
- if (energyUws == null) {
- return;
- }
-
- for (int i = energyUws.length - 1; i >= 0; i--) {
- long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
- ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
- if (energyDelta < 0) {
- // Likely, restart of powerstats HAL
- energyDelta = 0;
- }
- mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
- mLastConsumedEnergyUws[i] = energyUws[i];
- }
- }
-
@Override
protected void onUidRemoved(int uid) {
mLastTopActivityTime.delete(uid);
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
index 90981ada..7a84b05 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
@@ -28,12 +28,11 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.WifiPowerStatsLayout;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class WifiPowerStatsCollector extends PowerStatsCollector {
@@ -41,15 +40,13 @@
private static final long WIFI_ACTIVITY_REQUEST_TIMEOUT = 20000;
- private static final long ENERGY_UNSPECIFIED = -1;
-
interface Observer {
void onWifiPowerStatsRetrieved(WifiActivityEnergyInfo info,
List<BatteryStatsImpl.NetworkStatsDelta> delta, long elapsedRealtimeMs,
long uptimeMs);
}
- interface WifiStatsRetriever {
+ public interface WifiStatsRetriever {
interface Callback {
void onWifiScanTime(int uid, long scanTimeMs, long batchScanTimeMs);
}
@@ -58,14 +55,13 @@
long getWifiActiveDuration();
}
- interface Injector {
+ public interface Injector {
Handler getHandler();
Clock getClock();
PowerStatsUidResolver getUidResolver();
long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
PackageManager getPackageManager();
ConsumedEnergyRetriever getConsumedEnergyRetriever();
- IntSupplier getVoltageSupplier();
Supplier<NetworkStats> getWifiNetworkStatsSupplier();
WifiManager getWifiManager();
WifiStatsRetriever getWifiStatsRetriever();
@@ -83,13 +79,9 @@
private volatile WifiManager mWifiManager;
private volatile Supplier<NetworkStats> mNetworkStatsSupplier;
private volatile WifiStatsRetriever mWifiStatsRetriever;
- private ConsumedEnergyRetriever mConsumedEnergyRetriever;
- private IntSupplier mVoltageSupplier;
- private int[] mEnergyConsumerIds = new int[0];
+ private ConsumedEnergyHelper mConsumedEnergyHelper;
private WifiActivityEnergyInfo mLastWifiActivityInfo;
private NetworkStats mLastNetworkStats;
- private long[] mLastConsumedEnergyUws;
- private int mLastVoltageMv;
private static class WifiScanTimes {
public long basicScanTimeMs;
@@ -99,7 +91,7 @@
private final SparseArray<WifiScanTimes> mLastScanTimes = new SparseArray<>();
private long mLastWifiActiveDuration;
- WifiPowerStatsCollector(Injector injector, Observer observer) {
+ public WifiPowerStatsCollector(Injector injector, Observer observer) {
super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
BatteryConsumer.powerComponentIdToString(
BatteryConsumer.POWER_COMPONENT_WIFI)),
@@ -128,25 +120,17 @@
return false;
}
- mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
- mVoltageSupplier = mInjector.getVoltageSupplier();
mWifiManager = mInjector.getWifiManager();
mNetworkStatsSupplier = mInjector.getWifiNetworkStatsSupplier();
mWifiStatsRetriever = mInjector.getWifiStatsRetriever();
mPowerReportingSupported =
mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported();
- mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI);
- mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
- Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+ mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+ EnergyConsumerType.WIFI);
- mLayout = new WifiPowerStatsLayout();
- mLayout.addDeviceWifiActivity(mPowerReportingSupported);
- mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
- mLayout.addUidNetworkStats();
- mLayout.addDeviceSectionUsageDuration();
- mLayout.addDeviceSectionPowerEstimate();
- mLayout.addUidSectionPowerEstimate();
+ mLayout = new WifiPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount(),
+ mPowerReportingSupported);
PersistableBundle extras = new PersistableBundle();
mLayout.toExtras(extras);
@@ -162,7 +146,7 @@
}
@Override
- protected PowerStats collectStats() {
+ public PowerStats collectStats() {
if (!ensureInitialized()) {
return null;
}
@@ -176,9 +160,7 @@
List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas = collectNetworkStats();
collectWifiScanTime();
- if (mEnergyConsumerIds.length != 0) {
- collectEnergyConsumers();
- }
+ mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
if (mObserver != null) {
mObserver.onWifiPowerStatsRetrieved(activityInfo, networkStatsDeltas,
@@ -318,34 +300,6 @@
mLayout.setDeviceBatchedScanTime(mDeviceStats, mScanTimes.batchedScanTimeMs);
}
- private void collectEnergyConsumers() {
- int voltageMv = mVoltageSupplier.getAsInt();
- if (voltageMv <= 0) {
- Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
- + " mV) when querying energy consumers");
- return;
- }
-
- int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
- mLastVoltageMv = voltageMv;
-
- long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
- if (energyUws == null) {
- return;
- }
-
- for (int i = energyUws.length - 1; i >= 0; i--) {
- long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
- ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
- if (energyDelta < 0) {
- // Likely, restart of powerstats HAL
- energyDelta = 0;
- }
- mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
- mLastConsumedEnergyUws[i] = energyUws[i];
- }
- }
-
@Override
protected void onUidRemoved(int uid) {
super.onUidRemoved(uid);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/services/core/java/com/android/server/power/stats/format/AmbientDisplayPowerStatsLayout.java
similarity index 73%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to services/core/java/com/android/server/power/stats/format/AmbientDisplayPowerStatsLayout.java
index 3c5beeb..1b99b0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/services/core/java/com/android/server/power/stats/format/AmbientDisplayPowerStatsLayout.java
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.server.power.stats.format;
-package com.android.wm.shell.common.bubbles;
-
-parcelable BubbleBarLocation;
\ No newline at end of file
+public class AmbientDisplayPowerStatsLayout extends PowerStatsLayout {
+ public AmbientDisplayPowerStatsLayout() {
+ addDeviceSectionPowerEstimate();
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/BinaryStatePowerStatsLayout.java
similarity index 68%
rename from services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/BinaryStatePowerStatsLayout.java
index 502337c..4a26d83 100644
--- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/BinaryStatePowerStatsLayout.java
@@ -14,11 +14,17 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
-class BinaryStatePowerStatsLayout extends EnergyConsumerPowerStatsLayout {
- BinaryStatePowerStatsLayout() {
+import com.android.internal.os.PowerStats;
+
+public class BinaryStatePowerStatsLayout extends EnergyConsumerPowerStatsLayout {
+ public BinaryStatePowerStatsLayout() {
addDeviceSectionUsageDuration();
addUidSectionUsageDuration();
}
+
+ public BinaryStatePowerStatsLayout(PowerStats.Descriptor descriptor) {
+ super(descriptor);
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/BluetoothPowerStatsLayout.java
similarity index 88%
rename from services/core/java/com/android/server/power/stats/BluetoothPowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/BluetoothPowerStatsLayout.java
index 9358b5e..534a9f7 100644
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/BluetoothPowerStatsLayout.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
import android.annotation.NonNull;
import android.os.PersistableBundle;
@@ -37,21 +37,49 @@
private int mUidTxBytesPosition;
private int mUidScanTimePosition;
- BluetoothPowerStatsLayout() {
+ public BluetoothPowerStatsLayout(int energyConsumerCount) {
+ addDeviceBluetoothControllerActivity();
+ addDeviceSectionEnergyConsumers(energyConsumerCount);
+ addDeviceSectionUsageDuration();
+ addDeviceSectionPowerEstimate();
+ addUidTrafficStats();
+ addUidSectionPowerEstimate();
}
- BluetoothPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+ public BluetoothPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
super(descriptor);
+ PersistableBundle extras = descriptor.extras;
+ mDeviceRxTimePosition = extras.getInt(EXTRA_DEVICE_RX_TIME_POSITION);
+ mDeviceTxTimePosition = extras.getInt(EXTRA_DEVICE_TX_TIME_POSITION);
+ mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
+ mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
+ mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
+ mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
+ mUidScanTimePosition = extras.getInt(EXTRA_UID_SCAN_TIME_POSITION);
}
- void addDeviceBluetoothControllerActivity() {
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_RX_TIME_POSITION, mDeviceRxTimePosition);
+ extras.putInt(EXTRA_DEVICE_TX_TIME_POSITION, mDeviceTxTimePosition);
+ extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
+ extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
+ extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
+ extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
+ extras.putInt(EXTRA_UID_SCAN_TIME_POSITION, mUidScanTimePosition);
+ }
+
+ private void addDeviceBluetoothControllerActivity() {
mDeviceRxTimePosition = addDeviceSection(1, "rx");
mDeviceTxTimePosition = addDeviceSection(1, "tx");
mDeviceIdleTimePosition = addDeviceSection(1, "idle");
mDeviceScanTimePosition = addDeviceSection(1, "scan", FLAG_OPTIONAL);
}
- void addUidTrafficStats() {
+ private void addUidTrafficStats() {
mUidRxBytesPosition = addUidSection(1, "rx-B");
mUidTxBytesPosition = addUidSection(1, "tx-B");
mUidScanTimePosition = addUidSection(1, "scan", FLAG_OPTIONAL);
@@ -112,32 +140,4 @@
public long getUidScanTime(long[] stats) {
return stats[mUidScanTimePosition];
}
-
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- super.toExtras(extras);
- extras.putInt(EXTRA_DEVICE_RX_TIME_POSITION, mDeviceRxTimePosition);
- extras.putInt(EXTRA_DEVICE_TX_TIME_POSITION, mDeviceTxTimePosition);
- extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
- extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
- extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
- extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
- extras.putInt(EXTRA_UID_SCAN_TIME_POSITION, mUidScanTimePosition);
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
- mDeviceRxTimePosition = extras.getInt(EXTRA_DEVICE_RX_TIME_POSITION);
- mDeviceTxTimePosition = extras.getInt(EXTRA_DEVICE_TX_TIME_POSITION);
- mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
- mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
- mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
- mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
- mUidScanTimePosition = extras.getInt(EXTRA_UID_SCAN_TIME_POSITION);
- }
}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/CpuPowerStatsLayout.java
similarity index 85%
rename from services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/CpuPowerStatsLayout.java
index 2a02bd0..3186d7d 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/CpuPowerStatsLayout.java
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
+import android.annotation.NonNull;
import android.os.PersistableBundle;
+import com.android.internal.os.PowerStats;
+
/**
* Captures the positions and lengths of sections of the stats array, such as time-in-state,
* power usage estimates etc.
@@ -40,10 +43,59 @@
private int[] mScalingStepToPowerBracketMap;
+ public CpuPowerStatsLayout(int energyConsumerCount, int cpuScalingPolicyCount,
+ int[] scalingStepToPowerBracketMap) {
+ addDeviceSectionCpuTimeByScalingStep(scalingStepToPowerBracketMap.length);
+ addDeviceSectionCpuTimeByCluster(cpuScalingPolicyCount);
+ addDeviceSectionUsageDuration();
+ addDeviceSectionEnergyConsumers(energyConsumerCount);
+ addDeviceSectionPowerEstimate();
+ addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap);
+ addUidSectionPowerEstimate();
+ }
+
+ public CpuPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+ super(descriptor);
+ PersistableBundle extras = descriptor.extras;
+ mDeviceCpuTimeByScalingStepPosition =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
+ mDeviceCpuTimeByScalingStepCount =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
+ mDeviceCpuTimeByClusterPosition =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
+ mDeviceCpuTimeByClusterCount =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
+ mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
+ mScalingStepToPowerBracketMap =
+ getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
+ if (mScalingStepToPowerBracketMap == null) {
+ mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
+ }
+ updatePowerBracketCount();
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
+ mDeviceCpuTimeByScalingStepPosition);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
+ mDeviceCpuTimeByScalingStepCount);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
+ mDeviceCpuTimeByClusterPosition);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
+ mDeviceCpuTimeByClusterCount);
+ extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
+ putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
+ mScalingStepToPowerBracketMap);
+ }
+
/**
* Declare that the stats array has a section capturing CPU time per scaling step
*/
- public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
+ private void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount, "steps");
mDeviceCpuTimeByScalingStepCount = scalingStepCount;
}
@@ -71,7 +123,7 @@
/**
* Declare that the stats array has a section capturing CPU time in each cluster
*/
- public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
+ private void addDeviceSectionCpuTimeByCluster(int clusterCount) {
mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount, "clusters");
mDeviceCpuTimeByClusterCount = clusterCount;
}
@@ -99,7 +151,7 @@
/**
* Declare that the UID stats array has a section capturing CPU time per power bracket.
*/
- public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
+ private void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
updatePowerBracketCount();
mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount, "time");
@@ -135,44 +187,4 @@
public long getUidTimeByPowerBracket(long[] stats, int bracket) {
return stats[mUidPowerBracketsPosition + bracket];
}
-
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- super.toExtras(extras);
- extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
- mDeviceCpuTimeByScalingStepPosition);
- extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
- mDeviceCpuTimeByScalingStepCount);
- extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
- mDeviceCpuTimeByClusterPosition);
- extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
- mDeviceCpuTimeByClusterCount);
- extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
- putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
- mScalingStepToPowerBracketMap);
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
- mDeviceCpuTimeByScalingStepPosition =
- extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
- mDeviceCpuTimeByScalingStepCount =
- extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
- mDeviceCpuTimeByClusterPosition =
- extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
- mDeviceCpuTimeByClusterCount =
- extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
- mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
- mScalingStepToPowerBracketMap =
- getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
- if (mScalingStepToPowerBracketMap == null) {
- mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
- }
- updatePowerBracketCount();
- }
}
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/EnergyConsumerPowerStatsLayout.java
similarity index 80%
rename from services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/EnergyConsumerPowerStatsLayout.java
index 8430f56..e7a4822 100644
--- a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/EnergyConsumerPowerStatsLayout.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
-class EnergyConsumerPowerStatsLayout extends PowerStatsLayout {
- EnergyConsumerPowerStatsLayout() {
+import com.android.internal.os.PowerStats;
+
+public class EnergyConsumerPowerStatsLayout extends PowerStatsLayout {
+ public EnergyConsumerPowerStatsLayout() {
// Add a section for consumed energy, even if the specific device does not
// have support EnergyConsumers. This is done to guarantee format compatibility between
// PowerStats created by a PowerStatsCollector and those produced by a PowerStatsProcessor.
@@ -30,4 +32,8 @@
addUidSectionEnergyConsumers(1);
addUidSectionPowerEstimate();
}
+
+ public EnergyConsumerPowerStatsLayout(PowerStats.Descriptor descriptor) {
+ super(descriptor);
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/GnssPowerStatsLayout.java
similarity index 81%
rename from services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/GnssPowerStatsLayout.java
index 9a1317d..b70b173 100644
--- a/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/GnssPowerStatsLayout.java
@@ -14,28 +14,31 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
+import android.annotation.NonNull;
import android.location.GnssSignalQuality;
import android.os.PersistableBundle;
-class GnssPowerStatsLayout extends BinaryStatePowerStatsLayout {
+import com.android.internal.os.PowerStats;
+
+public class GnssPowerStatsLayout extends BinaryStatePowerStatsLayout {
private static final String EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION = "dt-sig";
private static final String EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION = "ut-sig";
- private int mDeviceSignalLevelTimePosition;
- private int mUidSignalLevelTimePosition;
+ private final int mDeviceSignalLevelTimePosition;
+ private final int mUidSignalLevelTimePosition;
- GnssPowerStatsLayout() {
+ public GnssPowerStatsLayout() {
mDeviceSignalLevelTimePosition = addDeviceSection(
GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
mUidSignalLevelTimePosition = addUidSection(
GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
}
- @Override
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
+ public GnssPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+ super(descriptor);
+ PersistableBundle extras = descriptor.extras;
mDeviceSignalLevelTimePosition = extras.getInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION);
mUidSignalLevelTimePosition = extras.getInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION);
}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/MobileRadioPowerStatsLayout.java
similarity index 79%
rename from services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/MobileRadioPowerStatsLayout.java
index 07d78f8..da6fc41 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/MobileRadioPowerStatsLayout.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
import android.annotation.NonNull;
+import android.os.BatteryStats;
import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
import android.telephony.ModemActivityInfo;
import android.util.Slog;
import android.util.SparseArray;
@@ -28,7 +30,7 @@
* Captures the positions and lengths of sections of the stats array, such as time-in-state,
* power usage estimates etc.
*/
-class MobileRadioPowerStatsLayout extends PowerStatsLayout {
+public class MobileRadioPowerStatsLayout extends PowerStatsLayout {
private static final String TAG = "MobileRadioPowerStatsLayout";
private static final String EXTRA_DEVICE_SLEEP_TIME_POSITION = "dt-sleep";
private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
@@ -56,27 +58,95 @@
private int mUidRxPacketsPosition;
private int mUidTxPacketsPosition;
- MobileRadioPowerStatsLayout() {
+ public MobileRadioPowerStatsLayout(int energyConsumerCount) {
+ addDeviceMobileActivity();
+ addDeviceSectionEnergyConsumers(energyConsumerCount);
+ addStateStats();
+ addUidNetworkStats();
+ addDeviceSectionUsageDuration();
+ addDeviceSectionPowerEstimate();
+ addUidSectionPowerEstimate();
}
- MobileRadioPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+ public MobileRadioPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
super(descriptor);
+ PersistableBundle extras = descriptor.extras;
+ mDeviceSleepTimePosition = extras.getInt(EXTRA_DEVICE_SLEEP_TIME_POSITION);
+ mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
+ mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
+ mDeviceCallTimePosition = extras.getInt(EXTRA_DEVICE_CALL_TIME_POSITION);
+ mDeviceCallPowerPosition = extras.getInt(EXTRA_DEVICE_CALL_POWER_POSITION);
+ mStateRxTimePosition = extras.getInt(EXTRA_STATE_RX_TIME_POSITION);
+ mStateTxTimesPosition = extras.getInt(EXTRA_STATE_TX_TIMES_POSITION);
+ mStateTxTimesCount = extras.getInt(EXTRA_STATE_TX_TIMES_COUNT);
+ mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
+ mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
+ mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
+ mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
}
- void addDeviceMobileActivity() {
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_SLEEP_TIME_POSITION, mDeviceSleepTimePosition);
+ extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
+ extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
+ extras.putInt(EXTRA_DEVICE_CALL_TIME_POSITION, mDeviceCallTimePosition);
+ extras.putInt(EXTRA_DEVICE_CALL_POWER_POSITION, mDeviceCallPowerPosition);
+ extras.putInt(EXTRA_STATE_RX_TIME_POSITION, mStateRxTimePosition);
+ extras.putInt(EXTRA_STATE_TX_TIMES_POSITION, mStateTxTimesPosition);
+ extras.putInt(EXTRA_STATE_TX_TIMES_COUNT, mStateTxTimesCount);
+ extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
+ extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
+ extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
+ extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
+ }
+
+ public static int makeStateKey(int rat, int freqRange) {
+ if (rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR) {
+ return rat | (freqRange << 8);
+ } else {
+ return rat;
+ }
+ }
+
+ @BatteryStats.RadioAccessTechnology
+ public static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
+ @AccessNetworkConstants.RadioAccessNetworkType int networkType) {
+ switch (networkType) {
+ case AccessNetworkConstants.AccessNetworkType.NGRAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
+ case AccessNetworkConstants.AccessNetworkType.EUTRAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE;
+ case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.IWLAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ default:
+ Slog.w(TAG,
+ "Unhandled RadioAccessNetworkType (" + networkType + "), mapping to OTHER");
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ }
+ }
+
+ private void addDeviceMobileActivity() {
mDeviceSleepTimePosition = addDeviceSection(1, "sleep");
mDeviceIdleTimePosition = addDeviceSection(1, "idle");
mDeviceScanTimePosition = addDeviceSection(1, "scan");
mDeviceCallTimePosition = addDeviceSection(1, "call", FLAG_OPTIONAL);
}
- void addStateStats() {
+ private void addStateStats() {
mStateRxTimePosition = addStateSection(1, "rx");
mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels();
mStateTxTimesPosition = addStateSection(mStateTxTimesCount, "tx");
}
- void addUidNetworkStats() {
+ private void addUidNetworkStats() {
mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
mUidRxBytesPosition = addUidSection(1, "rx-B");
mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
@@ -84,7 +154,7 @@
}
@Override
- public void addDeviceSectionPowerEstimate() {
+ protected void addDeviceSectionPowerEstimate() {
super.addDeviceSectionPowerEstimate();
// Printed as part of the PhoneCallPowerStatsProcessor
mDeviceCallPowerPosition = addDeviceSection(1, "call-power", FLAG_HIDDEN);
@@ -178,44 +248,6 @@
return stats[mUidTxPacketsPosition];
}
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- super.toExtras(extras);
- extras.putInt(EXTRA_DEVICE_SLEEP_TIME_POSITION, mDeviceSleepTimePosition);
- extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
- extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
- extras.putInt(EXTRA_DEVICE_CALL_TIME_POSITION, mDeviceCallTimePosition);
- extras.putInt(EXTRA_DEVICE_CALL_POWER_POSITION, mDeviceCallPowerPosition);
- extras.putInt(EXTRA_STATE_RX_TIME_POSITION, mStateRxTimePosition);
- extras.putInt(EXTRA_STATE_TX_TIMES_POSITION, mStateTxTimesPosition);
- extras.putInt(EXTRA_STATE_TX_TIMES_COUNT, mStateTxTimesCount);
- extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
- extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
- extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
- extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
- mDeviceSleepTimePosition = extras.getInt(EXTRA_DEVICE_SLEEP_TIME_POSITION);
- mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
- mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
- mDeviceCallTimePosition = extras.getInt(EXTRA_DEVICE_CALL_TIME_POSITION);
- mDeviceCallPowerPosition = extras.getInt(EXTRA_DEVICE_CALL_POWER_POSITION);
- mStateRxTimePosition = extras.getInt(EXTRA_STATE_RX_TIME_POSITION);
- mStateTxTimesPosition = extras.getInt(EXTRA_STATE_TX_TIMES_POSITION);
- mStateTxTimesCount = extras.getInt(EXTRA_STATE_TX_TIMES_COUNT);
- mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
- mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
- mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
- mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
- }
-
public void addRxTxTimesForRat(SparseArray<long[]> stateStats, int networkType, int freqRange,
long rxTime, int[] txTime) {
if (txTime.length != mStateTxTimesCount) {
@@ -239,9 +271,8 @@
return;
}
- int rat = MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology(
- networkType);
- int stateKey = MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange);
+ int rat = mapRadioAccessNetworkTypeToRadioAccessTechnology(networkType);
+ int stateKey = makeStateKey(rat, freqRange);
long[] stats = stateStats.get(stateKey);
if (stats == null) {
stats = new long[getStateStatsArrayLength()];
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/services/core/java/com/android/server/power/stats/format/PhoneCallPowerStatsLayout.java
similarity index 74%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to services/core/java/com/android/server/power/stats/format/PhoneCallPowerStatsLayout.java
index 3c5beeb..5a34148 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/services/core/java/com/android/server/power/stats/format/PhoneCallPowerStatsLayout.java
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.server.power.stats.format;
-package com.android.wm.shell.common.bubbles;
-
-parcelable BubbleBarLocation;
\ No newline at end of file
+public class PhoneCallPowerStatsLayout extends PowerStatsLayout {
+ public PhoneCallPowerStatsLayout() {
+ addDeviceSectionPowerEstimate();
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/PowerStatsLayout.java
similarity index 91%
rename from services/core/java/com/android/server/power/stats/PowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/PowerStatsLayout.java
index 62abfc6..d070919 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/PowerStatsLayout.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
import android.os.PersistableBundle;
import android.util.Slog;
@@ -36,7 +36,7 @@
private static final String EXTRA_UID_ENERGY_CONSUMERS_COUNT = "uec";
private static final String EXTRA_UID_POWER_POSITION = "up";
- protected static final int UNSUPPORTED = -1;
+ public static final int UNSUPPORTED = -1;
protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
protected static final int FLAG_OPTIONAL = 1;
protected static final int FLAG_HIDDEN = 2;
@@ -46,9 +46,9 @@
private int mStateStatsArrayLength;
private int mUidStatsArrayLength;
- private StringBuilder mDeviceFormat = new StringBuilder();
- private StringBuilder mStateFormat = new StringBuilder();
- private StringBuilder mUidFormat = new StringBuilder();
+ private final StringBuilder mDeviceFormat = new StringBuilder();
+ private final StringBuilder mStateFormat = new StringBuilder();
+ private final StringBuilder mUidFormat = new StringBuilder();
protected int mDeviceDurationPosition = UNSUPPORTED;
private int mDeviceEnergyConsumerPosition;
@@ -63,7 +63,32 @@
}
public PowerStatsLayout(PowerStats.Descriptor descriptor) {
- fromExtras(descriptor.extras);
+ PersistableBundle extras = descriptor.extras;
+ mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
+ mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
+ mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
+ mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+ mUidDurationPosition = extras.getInt(EXTRA_UID_DURATION_POSITION);
+ mUidEnergyConsumerPosition = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION);
+ mUidEnergyConsumerCount = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT);
+ mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION, mDeviceEnergyConsumerPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT, mDeviceEnergyConsumerCount);
+ extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
+ extras.putInt(EXTRA_UID_DURATION_POSITION, mUidDurationPosition);
+ extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION, mUidEnergyConsumerPosition);
+ extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT, mUidEnergyConsumerCount);
+ extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+ extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
+ extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
+ extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, mUidFormat.toString());
}
public int getDeviceStatsArrayLength() {
@@ -141,7 +166,7 @@
/**
* Declare that the stats array has a section capturing usage duration
*/
- public void addDeviceSectionUsageDuration() {
+ protected void addDeviceSectionUsageDuration() {
mDeviceDurationPosition = addDeviceSection(1, "usage", FLAG_OPTIONAL);
}
@@ -163,7 +188,7 @@
* Declares that the stats array has a section capturing EnergyConsumer data from
* PowerStatsService.
*/
- public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
+ protected void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy",
FLAG_OPTIONAL);
mDeviceEnergyConsumerCount = energyConsumerCount;
@@ -192,7 +217,7 @@
/**
* Declare that the stats array has a section capturing a power estimate
*/
- public void addDeviceSectionPowerEstimate() {
+ protected void addDeviceSectionPowerEstimate() {
mDevicePowerEstimatePosition = addDeviceSection(1, "power",
FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
}
@@ -215,14 +240,14 @@
/**
* Declare that the UID stats array has a section capturing usage duration
*/
- public void addUidSectionUsageDuration() {
+ protected void addUidSectionUsageDuration() {
mUidDurationPosition = addUidSection(1, "time");
}
/**
* Declare that the UID stats array has a section capturing a power estimate
*/
- public void addUidSectionPowerEstimate() {
+ protected void addUidSectionPowerEstimate() {
mUidPowerEstimatePosition = addUidSection(1, "power", FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
}
@@ -251,7 +276,7 @@
* Declares that the UID stats array has a section capturing EnergyConsumer data from
* PowerStatsService.
*/
- public void addUidSectionEnergyConsumers(int energyConsumerCount) {
+ protected void addUidSectionEnergyConsumers(int energyConsumerCount) {
mUidEnergyConsumerPosition = addUidSection(energyConsumerCount, "energy",
FLAG_OPTIONAL);
mUidEnergyConsumerCount = energyConsumerCount;
@@ -292,39 +317,6 @@
return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
}
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
- mDeviceEnergyConsumerPosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
- mDeviceEnergyConsumerCount);
- extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
- extras.putInt(EXTRA_UID_DURATION_POSITION, mUidDurationPosition);
- extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION, mUidEnergyConsumerPosition);
- extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT, mUidEnergyConsumerCount);
- extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
- extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
- extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
- extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, mUidFormat.toString());
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
- mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
- mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
- mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
- mUidDurationPosition = extras.getInt(EXTRA_UID_DURATION_POSITION);
- mUidEnergyConsumerPosition = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION);
- mUidEnergyConsumerCount = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT);
- mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
- }
-
protected void putIntArray(PersistableBundle extras, String key, int[] array) {
if (array == null) {
return;
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java
similarity index 89%
rename from services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java
index f134aa8..6f6a7ff 100644
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
import android.annotation.NonNull;
import android.os.BatteryStats;
@@ -41,14 +41,40 @@
private int mDeviceScreenDozePowerPosition;
private int mUidTopActivityTimePosition;
- ScreenPowerStatsLayout() {
+ public ScreenPowerStatsLayout(int energyConsumerCount, int displayCount) {
+ addDeviceScreenUsageDurationSection(displayCount);
+ addDeviceSectionEnergyConsumers(energyConsumerCount);
+ addDeviceSectionUsageDuration();
+ addDeviceSectionPowerEstimate();
+ addUidTopActivitiyDuration();
+ addUidSectionPowerEstimate();
}
- ScreenPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+ public ScreenPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
super(descriptor);
+ PersistableBundle extras = descriptor.extras;
+ mDisplayCount = extras.getInt(EXTRA_DEVICE_SCREEN_COUNT, 1);
+ mDeviceScreenOnDurationPosition = extras.getInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION);
+ mDeviceBrightnessDurationPositions = extras.getIntArray(
+ EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS);
+ mDeviceScreenDozeDurationPosition = extras.getInt(EXTRA_DEVICE_DOZE_DURATION_POSITION);
+ mDeviceScreenDozePowerPosition = extras.getInt(EXTRA_DEVICE_DOZE_POWER_POSITION);
+ mUidTopActivityTimePosition = extras.getInt(EXTRA_UID_FOREGROUND_DURATION);
}
- void addDeviceScreenUsageDurationSection(int displayCount) {
+ @Override
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_SCREEN_COUNT, mDisplayCount);
+ extras.putInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION, mDeviceScreenOnDurationPosition);
+ extras.putIntArray(EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS,
+ mDeviceBrightnessDurationPositions);
+ extras.putInt(EXTRA_DEVICE_DOZE_DURATION_POSITION, mDeviceScreenDozeDurationPosition);
+ extras.putInt(EXTRA_DEVICE_DOZE_POWER_POSITION, mDeviceScreenDozePowerPosition);
+ extras.putInt(EXTRA_UID_FOREGROUND_DURATION, mUidTopActivityTimePosition);
+ }
+
+ private void addDeviceScreenUsageDurationSection(int displayCount) {
mDisplayCount = displayCount;
mDeviceScreenOnDurationPosition = addDeviceSection(displayCount, "on");
mDeviceBrightnessDurationPositions = new int[BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
@@ -60,7 +86,7 @@
}
@Override
- public void addDeviceSectionPowerEstimate() {
+ protected void addDeviceSectionPowerEstimate() {
super.addDeviceSectionPowerEstimate();
// Used by AmbientDisplayPowerStatsProcessor
mDeviceScreenDozePowerPosition = addDeviceSection(1, "doze-power", FLAG_HIDDEN);
@@ -127,7 +153,7 @@
return stats[mDeviceScreenDozePowerPosition] / MILLI_TO_NANO_MULTIPLIER;
}
- void addUidTopActivitiyDuration() {
+ private void addUidTopActivitiyDuration() {
mUidTopActivityTimePosition = addUidSection(1, "top");
}
@@ -144,28 +170,4 @@
public long getUidTopActivityDuration(long[] stats) {
return stats[mUidTopActivityTimePosition];
}
-
- @Override
- public void toExtras(PersistableBundle extras) {
- super.toExtras(extras);
- extras.putInt(EXTRA_DEVICE_SCREEN_COUNT, mDisplayCount);
- extras.putInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION, mDeviceScreenOnDurationPosition);
- extras.putIntArray(EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS,
- mDeviceBrightnessDurationPositions);
- extras.putInt(EXTRA_DEVICE_DOZE_DURATION_POSITION, mDeviceScreenDozeDurationPosition);
- extras.putInt(EXTRA_DEVICE_DOZE_POWER_POSITION, mDeviceScreenDozePowerPosition);
- extras.putInt(EXTRA_UID_FOREGROUND_DURATION, mUidTopActivityTimePosition);
- }
-
- @Override
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
- mDisplayCount = extras.getInt(EXTRA_DEVICE_SCREEN_COUNT, 1);
- mDeviceScreenOnDurationPosition = extras.getInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION);
- mDeviceBrightnessDurationPositions = extras.getIntArray(
- EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS);
- mDeviceScreenDozeDurationPosition = extras.getInt(EXTRA_DEVICE_DOZE_DURATION_POSITION);
- mDeviceScreenDozePowerPosition = extras.getInt(EXTRA_DEVICE_DOZE_POWER_POSITION);
- mUidTopActivityTimePosition = extras.getInt(EXTRA_UID_FOREGROUND_DURATION);
- }
}
diff --git a/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java
similarity index 72%
rename from services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java
index e66cd39..e8df3dd 100644
--- a/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java
@@ -14,12 +14,17 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
import android.os.PersistableBundle;
import android.util.Slog;
import android.util.SparseIntArray;
+import com.android.internal.os.PowerStats;
+
+import java.util.Arrays;
+import java.util.Map;
+
public class SensorPowerStatsLayout extends PowerStatsLayout {
private static final String TAG = "SensorPowerStatsLayout";
private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dsh";
@@ -27,7 +32,48 @@
private final SparseIntArray mSensorPositions = new SparseIntArray();
- void addUidSensorSection(int handle, String label) {
+ public SensorPowerStatsLayout(Map<Integer, String> idToLabelMap) {
+ Integer[] keys = new Integer[idToLabelMap.size()];
+ idToLabelMap.keySet().toArray(keys);
+ Arrays.sort(keys);
+ for (int i = 0; i < keys.length; i++) {
+ addUidSensorSection(keys[i], idToLabelMap.get(keys[i]));
+ }
+ addUidSectionPowerEstimate();
+ addDeviceSectionPowerEstimate();
+ }
+
+ public SensorPowerStatsLayout(PowerStats.Descriptor descriptor) {
+ super(descriptor);
+
+ PersistableBundle extras = descriptor.extras;
+ int[] handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES);
+ int[] uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS);
+
+ if (handlers != null && uidDurationPositions != null) {
+ for (int i = 0; i < handlers.length; i++) {
+ mSensorPositions.put(handlers[i], uidDurationPositions[i]);
+ }
+ }
+ }
+
+ @Override
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+
+ int[] handlers = new int[mSensorPositions.size()];
+ int[] uidDurationPositions = new int[mSensorPositions.size()];
+
+ for (int i = 0; i < mSensorPositions.size(); i++) {
+ handlers[i] = mSensorPositions.keyAt(i);
+ uidDurationPositions[i] = mSensorPositions.valueAt(i);
+ }
+
+ extras.putIntArray(EXTRA_DEVICE_SENSOR_HANDLES, handlers);
+ extras.putIntArray(EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions);
+ }
+
+ private void addUidSensorSection(int handle, String label) {
mSensorPositions.put(handle, addUidSection(1, label, FLAG_OPTIONAL));
}
@@ -50,32 +96,4 @@
}
stats[position] += durationMs;
}
-
- @Override
- public void toExtras(PersistableBundle extras) {
- super.toExtras(extras);
-
- int[] handlers = new int[mSensorPositions.size()];
- int[] uidDurationPositions = new int[mSensorPositions.size()];
-
- for (int i = 0; i < mSensorPositions.size(); i++) {
- handlers[i] = mSensorPositions.keyAt(i);
- uidDurationPositions[i] = mSensorPositions.valueAt(i);
- }
-
- extras.putIntArray(EXTRA_DEVICE_SENSOR_HANDLES, handlers);
- extras.putIntArray(EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions);
- }
-
- @Override
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
-
- int[] handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES);
- int[] uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS);
-
- for (int i = 0; i < handlers.length; i++) {
- mSensorPositions.put(handlers[i], uidDurationPositions[i]);
- }
- }
}
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/WifiPowerStatsLayout.java
similarity index 93%
rename from services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
rename to services/core/java/com/android/server/power/stats/format/WifiPowerStatsLayout.java
index e2e8226..ce7ef12 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/WifiPowerStatsLayout.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.format;
import android.annotation.NonNull;
import android.os.PersistableBundle;
@@ -22,7 +22,6 @@
import com.android.internal.os.PowerStats;
public class WifiPowerStatsLayout extends PowerStatsLayout {
- private static final String TAG = "WifiPowerStatsLayout";
private static final int UNSPECIFIED = -1;
private static final String EXTRA_POWER_REPORTING_SUPPORTED = "prs";
private static final String EXTRA_DEVICE_RX_TIME_POSITION = "dt-rx";
@@ -54,14 +53,56 @@
private int mUidScanTimePosition;
private int mUidBatchScanTimePosition;
- WifiPowerStatsLayout() {
+ public WifiPowerStatsLayout(int energyConsumerCount, boolean powerReportingSupported) {
+ addDeviceWifiActivity(powerReportingSupported);
+ addDeviceSectionEnergyConsumers(energyConsumerCount);
+ addUidNetworkStats();
+ addDeviceSectionUsageDuration();
+ addDeviceSectionPowerEstimate();
+ addUidSectionPowerEstimate();
}
- WifiPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+ public WifiPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
super(descriptor);
+ PersistableBundle extras = descriptor.extras;
+ mPowerReportingSupported = extras.getBoolean(EXTRA_POWER_REPORTING_SUPPORTED);
+ mDeviceRxTimePosition = extras.getInt(EXTRA_DEVICE_RX_TIME_POSITION);
+ mDeviceTxTimePosition = extras.getInt(EXTRA_DEVICE_TX_TIME_POSITION);
+ mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
+ mDeviceBasicScanTimePosition = extras.getInt(EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION);
+ mDeviceBatchedScanTimePosition = extras.getInt(EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION);
+ mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
+ mDeviceActiveTimePosition = extras.getInt(EXTRA_DEVICE_ACTIVE_TIME_POSITION);
+ mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
+ mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
+ mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
+ mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
+ mUidScanTimePosition = extras.getInt(EXTRA_UID_SCAN_TIME_POSITION);
+ mUidBatchScanTimePosition = extras.getInt(EXTRA_UID_BATCH_SCAN_TIME_POSITION);
}
- void addDeviceWifiActivity(boolean powerReportingSupported) {
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putBoolean(EXTRA_POWER_REPORTING_SUPPORTED, mPowerReportingSupported);
+ extras.putInt(EXTRA_DEVICE_RX_TIME_POSITION, mDeviceRxTimePosition);
+ extras.putInt(EXTRA_DEVICE_TX_TIME_POSITION, mDeviceTxTimePosition);
+ extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
+ extras.putInt(EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION, mDeviceBasicScanTimePosition);
+ extras.putInt(EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION, mDeviceBatchedScanTimePosition);
+ extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
+ extras.putInt(EXTRA_DEVICE_ACTIVE_TIME_POSITION, mDeviceActiveTimePosition);
+ extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
+ extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
+ extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
+ extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
+ extras.putInt(EXTRA_UID_SCAN_TIME_POSITION, mUidScanTimePosition);
+ extras.putInt(EXTRA_UID_BATCH_SCAN_TIME_POSITION, mUidBatchScanTimePosition);
+ }
+
+ private void addDeviceWifiActivity(boolean powerReportingSupported) {
mPowerReportingSupported = powerReportingSupported;
if (mPowerReportingSupported) {
mDeviceActiveTimePosition = UNSPECIFIED;
@@ -80,7 +121,7 @@
mDeviceBatchedScanTimePosition = addDeviceSection(1, "batched-scan", FLAG_OPTIONAL);
}
- void addUidNetworkStats() {
+ private void addUidNetworkStats() {
mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
mUidRxBytesPosition = addUidSection(1, "rx-B");
mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
@@ -196,46 +237,4 @@
public long getUidBatchedScanTime(long[] stats) {
return stats[mUidBatchScanTimePosition];
}
-
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- super.toExtras(extras);
- extras.putBoolean(EXTRA_POWER_REPORTING_SUPPORTED, mPowerReportingSupported);
- extras.putInt(EXTRA_DEVICE_RX_TIME_POSITION, mDeviceRxTimePosition);
- extras.putInt(EXTRA_DEVICE_TX_TIME_POSITION, mDeviceTxTimePosition);
- extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
- extras.putInt(EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION, mDeviceBasicScanTimePosition);
- extras.putInt(EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION, mDeviceBatchedScanTimePosition);
- extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
- extras.putInt(EXTRA_DEVICE_ACTIVE_TIME_POSITION, mDeviceActiveTimePosition);
- extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
- extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
- extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
- extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
- extras.putInt(EXTRA_UID_SCAN_TIME_POSITION, mUidScanTimePosition);
- extras.putInt(EXTRA_UID_BATCH_SCAN_TIME_POSITION, mUidBatchScanTimePosition);
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
- mPowerReportingSupported = extras.getBoolean(EXTRA_POWER_REPORTING_SUPPORTED);
- mDeviceRxTimePosition = extras.getInt(EXTRA_DEVICE_RX_TIME_POSITION);
- mDeviceTxTimePosition = extras.getInt(EXTRA_DEVICE_TX_TIME_POSITION);
- mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
- mDeviceBasicScanTimePosition = extras.getInt(EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION);
- mDeviceBatchedScanTimePosition = extras.getInt(EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION);
- mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
- mDeviceActiveTimePosition = extras.getInt(EXTRA_DEVICE_ACTIVE_TIME_POSITION);
- mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
- mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
- mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
- mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
- mUidScanTimePosition = extras.getInt(EXTRA_UID_SCAN_TIME_POSITION);
- mUidBatchScanTimePosition = extras.getInt(EXTRA_UID_BATCH_SCAN_TIME_POSITION);
- }
}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java
similarity index 97%
rename from services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
rename to services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java
index 674b4bc..a4758dd 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.DurationMillisLong;
@@ -33,7 +33,7 @@
import com.android.internal.os.PowerStats;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.power.stats.AggregatedPowerStatsConfig.PowerComponent;
+import com.android.server.power.stats.processor.AggregatedPowerStatsConfig.PowerComponent;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -143,7 +143,8 @@
}
}
- List<ClockUpdate> getClockUpdates() {
+ // TODO - DO NOT SUBMIT public
+ public List<ClockUpdate> getClockUpdates() {
return mClockUpdates;
}
@@ -274,7 +275,8 @@
int powerComponentId = parser.getAttributeInt(null,
PowerComponentAggregatedPowerStats.XML_ATTR_ID);
- PowerComponentAggregatedPowerStats powerComponentStats =
+ PowerComponentAggregatedPowerStats
+ powerComponentStats =
stats.getPowerComponentStats(powerComponentId);
if (powerComponentStats == null) {
PowerComponent powerComponent =
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsConfig.java
similarity index 95%
rename from services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
rename to services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsConfig.java
index ec12228..eaeda43 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsConfig.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -32,7 +32,7 @@
* WiFi, etc). Also, it determines which states are tracked globally and which ones on a per-UID
* basis.
*/
-public class AggregatedPowerStatsConfig {
+class AggregatedPowerStatsConfig {
public static final int STATE_POWER = 0;
public static final int STATE_SCREEN = 1;
public static final int STATE_PROCESS_STATE = 2;
@@ -70,7 +70,7 @@
/**
* Configuration for a give power component (CPU, WiFi, etc)
*/
- public static class PowerComponent {
+ static class PowerComponent {
private final int mPowerComponentId;
private @TrackedState int[] mTrackedDeviceStates;
private @TrackedState int[] mTrackedUidStates;
@@ -174,7 +174,7 @@
* standard power component IDs, e.g. {@link BatteryConsumer#POWER_COMPONENT_CPU}, or
* a custom power component.
*/
- public PowerComponent trackPowerComponent(int powerComponentId) {
+ PowerComponent trackPowerComponent(int powerComponentId) {
PowerComponent builder = new PowerComponent(powerComponentId);
mPowerComponents.add(builder);
return builder;
@@ -185,7 +185,7 @@
* of a different power component. The tracked states will be the same as the parent
* component's.
*/
- public PowerComponent trackPowerComponent(int powerComponentId,
+ PowerComponent trackPowerComponent(int powerComponentId,
int parentPowerComponentId) {
PowerComponent parent = null;
for (int i = 0; i < mPowerComponents.size(); i++) {
@@ -211,7 +211,7 @@
* Creates a configuration for custom power components, which are yet to be discovered
* dynamically through the integration with PowerStatsService.
*/
- public PowerComponent trackCustomPowerComponents(
+ PowerComponent trackCustomPowerComponents(
Supplier<PowerStatsProcessor> processorFactory) {
mCustomPowerStatsProcessorFactory = processorFactory;
mCustomPowerComponent = new PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY);
@@ -221,7 +221,7 @@
/**
* Returns configurations for all registered or dynamically discovered power components.
*/
- public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
+ List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
return mPowerComponents;
}
@@ -230,7 +230,7 @@
* integration with PowerStatsService.
*/
@Nullable
- public PowerComponent createPowerComponent(int powerComponentId) {
+ PowerComponent createPowerComponent(int powerComponentId) {
if (mCustomPowerComponent == null) {
return null;
}
diff --git a/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsSection.java b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsSection.java
new file mode 100644
index 0000000..4a9730c
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsSection.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import android.util.IndentingPrintWriter;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.power.stats.PowerStatsSpan;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class AggregatedPowerStatsSection extends PowerStatsSpan.Section {
+ public static final String TYPE = "aggregated-power-stats";
+
+ private final AggregatedPowerStats mAggregatedPowerStats;
+
+ AggregatedPowerStatsSection(AggregatedPowerStats aggregatedPowerStats) {
+ super(TYPE);
+ mAggregatedPowerStats = aggregatedPowerStats;
+ }
+
+ public AggregatedPowerStats getAggregatedPowerStats() {
+ return mAggregatedPowerStats;
+ }
+
+ @Override
+ public void write(TypedXmlSerializer serializer) throws IOException {
+ mAggregatedPowerStats.writeXml(serializer);
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter ipw) {
+ mAggregatedPowerStats.dump(ipw);
+ }
+
+ static class Reader implements PowerStatsSpan.SectionReader {
+ private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+
+ Reader(AggregatedPowerStatsConfig config) {
+ mAggregatedPowerStatsConfig = config;
+ }
+
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ @Override
+ public PowerStatsSpan.Section read(String sectionType, TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return new AggregatedPowerStatsSection(
+ AggregatedPowerStats.createFromXml(parser, mAggregatedPowerStatsConfig));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
similarity index 86%
rename from services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
index a42929f..32dfdf9 100644
--- a/services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
@@ -13,24 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.AmbientDisplayPowerStatsLayout;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
-public class AmbientDisplayPowerStatsProcessor extends PowerStatsProcessor {
- private final PowerStatsLayout mStatsLayout;
+class AmbientDisplayPowerStatsProcessor extends PowerStatsProcessor {
+ private final AmbientDisplayPowerStatsLayout mStatsLayout;
private final PowerStats.Descriptor mDescriptor;
private final long[] mTmpDeviceStats;
private PowerStats.Descriptor mScreenPowerStatsDescriptor;
private ScreenPowerStatsLayout mScreenPowerStatsLayout;
private long[] mTmpScreenStats;
- public AmbientDisplayPowerStatsProcessor() {
- mStatsLayout = new PowerStatsLayout();
- mStatsLayout.addDeviceSectionPowerEstimate();
+ AmbientDisplayPowerStatsProcessor() {
+ mStatsLayout = new AmbientDisplayPowerStatsLayout();
PersistableBundle extras = new PersistableBundle();
mStatsLayout.toExtras(extras);
mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
diff --git a/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java
similarity index 82%
rename from services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java
index a48f162..ad1b4a7 100644
--- a/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerStatsUidResolver;
-public class AudioPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
- public AudioPowerStatsProcessor(PowerProfile powerProfile,
+class AudioPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ AudioPowerStatsProcessor(PowerProfile powerProfile,
PowerStatsUidResolver uidResolver) {
super(BatteryConsumer.POWER_COMPONENT_AUDIO, uidResolver,
powerProfile.getAveragePower(PowerProfile.POWER_AUDIO));
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java
similarity index 97%
rename from services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java
index 03df46a..e45a707 100644
--- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.annotation.IntDef;
import android.os.BatteryStats;
@@ -22,6 +22,9 @@
import android.os.Process;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java
similarity index 97%
rename from services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java
index 077b057..4c1a0db 100644
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
import java.util.ArrayList;
import java.util.List;
-public class BluetoothPowerStatsProcessor extends PowerStatsProcessor {
- private static final String TAG = "BluetoothPowerStatsProcessor";
-
+class BluetoothPowerStatsProcessor extends PowerStatsProcessor {
private final UsageBasedPowerEstimator mRxPowerEstimator;
private final UsageBasedPowerEstimator mTxPowerEstimator;
private final UsageBasedPowerEstimator mIdlePowerEstimator;
@@ -37,7 +37,7 @@
private long[] mTmpDeviceStatsArray;
private long[] mTmpUidStatsArray;
- public BluetoothPowerStatsProcessor(PowerProfile powerProfile) {
+ BluetoothPowerStatsProcessor(PowerProfile powerProfile) {
mRxPowerEstimator = new UsageBasedPowerEstimator(
powerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX));
mTxPowerEstimator = new UsageBasedPowerEstimator(
diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java
similarity index 82%
rename from services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java
index 15c3eb8..8309061 100644
--- a/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerStatsUidResolver;
-public class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
- public CameraPowerStatsProcessor(PowerProfile powerProfile,
+class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ CameraPowerStatsProcessor(PowerProfile powerProfile,
PowerStatsUidResolver uidResolver) {
super(BatteryConsumer.POWER_COMPONENT_CAMERA, uidResolver,
powerProfile.getAveragePower(PowerProfile.POWER_CAMERA));
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
similarity index 97%
rename from services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
index 6da0a8f..5f7a3da 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.util.ArraySet;
import android.util.Log;
@@ -22,13 +22,14 @@
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
-public class CpuPowerStatsProcessor extends PowerStatsProcessor {
+class CpuPowerStatsProcessor extends PowerStatsProcessor {
private static final String TAG = "CpuPowerStatsProcessor";
private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
@@ -72,7 +73,7 @@
// Temp array for retrieval of UID power stats, to avoid repeated allocations
private long[] mTmpUidStatsArray;
- public CpuPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) {
+ CpuPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) {
mCpuScalingPolicies = scalingPolicies;
mCpuScalingStepCount = scalingPolicies.getScalingStepCount();
mScalingStepToCluster = new int[mCpuScalingStepCount];
@@ -104,8 +105,7 @@
}
mLastUsedDescriptor = descriptor;
- mStatsLayout = new CpuPowerStatsLayout();
- mStatsLayout.fromExtras(descriptor.extras);
+ mStatsLayout = new CpuPowerStatsLayout(descriptor);
mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
@@ -138,7 +138,7 @@
}
@Override
- public void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
if (stats.getPowerStatsDescriptor() == null) {
return;
}
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java
similarity index 94%
rename from services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java
index a86242a..76adc47 100644
--- a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
import java.util.ArrayList;
import java.util.List;
-public class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
+class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
private static final EnergyConsumerPowerStatsLayout sLayout =
new EnergyConsumerPowerStatsLayout();
private long[] mTmpDeviceStatsArray;
diff --git a/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java
similarity index 82%
rename from services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java
index f7216c9..b0bef69 100644
--- a/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerStatsUidResolver;
-public class FlashlightPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
- public FlashlightPowerStatsProcessor(PowerProfile powerProfile,
+class FlashlightPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ FlashlightPowerStatsProcessor(PowerProfile powerProfile,
PowerStatsUidResolver uidResolver) {
super(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, uidResolver,
powerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT));
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java
similarity index 94%
rename from services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java
index 0b28710..f1e3e90 100644
--- a/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.location.GnssSignalQuality;
import android.os.BatteryConsumer;
@@ -23,10 +23,13 @@
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.GnssPowerStatsLayout;
import java.util.Arrays;
-public class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
private static final GnssPowerStatsLayout sStatsLayout = new GnssPowerStatsLayout();
private final UsageBasedPowerEstimator[] mSignalLevelEstimators =
new UsageBasedPowerEstimator[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
@@ -37,7 +40,7 @@
private final long[] mGnssSignalDurations =
new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
- public GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
+ GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver,
powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON),
sStatsLayout);
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
similarity index 96%
rename from services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
index dcce562..b4c40de8 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.os.BatteryStats;
import android.telephony.CellSignalStrength;
@@ -26,13 +26,15 @@
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
import com.android.internal.power.ModemPowerProfile;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
- private static final String TAG = "MobileRadioPowerStatsProcessor";
+class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
+ private static final String TAG = "MobileRadioPowerStats";
private static final boolean DEBUG = false;
private static final int NUM_SIGNAL_STRENGTH_LEVELS =
@@ -61,7 +63,7 @@
private long[] mTmpStateStatsArray;
private long[] mTmpUidStatsArray;
- public MobileRadioPowerStatsProcessor(PowerProfile powerProfile) {
+ MobileRadioPowerStatsProcessor(PowerProfile powerProfile) {
final double sleepDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP,
Double.NaN);
@@ -101,7 +103,7 @@
? ServiceState.FREQUENCY_RANGE_COUNT : 1;
for (int freqRange = 0; freqRange < freqCount; freqRange++) {
mRxTxPowerEstimators.put(
- MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange),
+ MobileRadioPowerStatsLayout.makeStateKey(rat, freqRange),
buildRxTxPowerEstimators(powerProfile, rat, freqRange));
}
}
@@ -114,8 +116,7 @@
ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE);
double rxDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(rxKey, Double.NaN);
if (Double.isNaN(rxDrainRateMa)) {
- Log.w(TAG, "Unavailable Power Profile constant for key 0x"
- + Long.toHexString(rxKey));
+ Log.w(TAG, "Unavailable Power Profile constant for key 0x" + Long.toHexString(rxKey));
rxDrainRateMa = 0;
}
estimators.mRxPowerEstimator = new UsageBasedPowerEstimator(rxDrainRateMa);
diff --git a/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java b/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java
new file mode 100644
index 0000000..2ba4a52
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java
@@ -0,0 +1,310 @@
+/*
+ * 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.server.power.stats.processor;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerAttributor;
+import com.android.server.power.stats.PowerStatsSpan;
+import com.android.server.power.stats.PowerStatsStore;
+import com.android.server.power.stats.PowerStatsUidResolver;
+
+import java.util.List;
+
+public class MultiStatePowerAttributor implements PowerAttributor {
+ private static final String TAG = "MultiStatePowerAttributor";
+
+ private final PowerStatsStore mPowerStatsStore;
+ private final PowerStatsExporter mPowerStatsExporter;
+ private final PowerStatsAggregator mPowerStatsAggregator;
+ private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray();
+
+ // TODO(b/346371828): remove dependency on PowerStatsUidResolver. At the time of power
+ // attribution isolates UIDs are supposed to be long forgotten.
+ public MultiStatePowerAttributor(Context context, PowerStatsStore powerStatsStore,
+ @NonNull PowerProfile powerProfile, @NonNull CpuScalingPolicies cpuScalingPolicies,
+ @NonNull PowerStatsUidResolver powerStatsUidResolver) {
+ this(powerStatsStore, new PowerStatsAggregator(
+ createAggregatedPowerStatsConfig(context, powerProfile, cpuScalingPolicies,
+ powerStatsUidResolver)));
+ }
+
+ @VisibleForTesting
+ MultiStatePowerAttributor(PowerStatsStore powerStatsStore,
+ PowerStatsAggregator powerStatsAggregator) {
+ mPowerStatsStore = powerStatsStore;
+ mPowerStatsAggregator = powerStatsAggregator;
+ mPowerStatsStore.addSectionReader(
+ new AggregatedPowerStatsSection.Reader(mPowerStatsAggregator.getConfig()));
+ mPowerStatsExporter = new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator);
+ }
+
+ private static AggregatedPowerStatsConfig createAggregatedPowerStatsConfig(Context context,
+ PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies,
+ PowerStatsUidResolver powerStatsUidResolver) {
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new CpuPowerStatsProcessor(powerProfile, cpuScalingPolicies));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .setProcessorSupplier(
+ () -> new ScreenPowerStatsProcessor(powerProfile));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+ BatteryConsumer.POWER_COMPONENT_SCREEN)
+ .setProcessorSupplier(AmbientDisplayPowerStatsProcessor::new);
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new MobileRadioPowerStatsProcessor(powerProfile));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .setProcessorSupplier(PhoneCallPowerStatsProcessor::new);
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_WIFI)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new WifiPowerStatsProcessor(powerProfile));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new BluetoothPowerStatsProcessor(powerProfile));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AUDIO)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new AudioPowerStatsProcessor(powerProfile, powerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new VideoPowerStatsProcessor(powerProfile, powerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new FlashlightPowerStatsProcessor(powerProfile,
+ powerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new CameraPowerStatsProcessor(powerProfile, powerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new GnssPowerStatsProcessor(powerProfile, powerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessorSupplier(() -> new SensorPowerStatsProcessor(
+ () -> context.getSystemService(SensorManager.class)));
+
+ config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+ return config;
+ }
+
+ /**
+ * Marks the specified power component as supported by this PowerAttributor
+ */
+ public void setPowerComponentSupported(@BatteryConsumer.PowerComponentId int powerComponentId,
+ boolean enabled) {
+ mPowerStatsExporterEnabled.put(powerComponentId, enabled);
+ mPowerStatsExporter.setPowerComponentEnabled(powerComponentId, enabled);
+ }
+
+ @Override
+ public boolean isPowerComponentSupported(
+ @BatteryConsumer.PowerComponentId int powerComponentId) {
+ return mPowerStatsExporterEnabled.get(powerComponentId);
+ }
+
+ @Override
+ public void estimatePowerConsumption(BatteryUsageStats.Builder batteryUsageStatsBuilder,
+ BatteryStatsHistory batteryHistory, long monotonicStartTime, long monotonicEndTime) {
+ mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder, batteryHistory,
+ monotonicStartTime, monotonicEndTime);
+ }
+
+ @Override
+ public void dumpEstimatedPowerConsumption(IndentingPrintWriter ipw,
+ BatteryStatsHistory batteryStatsHistory,
+ long startTime, long endTime) {
+ mPowerStatsAggregator.aggregatePowerStats(batteryStatsHistory, startTime, endTime,
+ stats -> {
+ // Create a PowerStatsSpan for consistency of the textual output
+ PowerStatsSpan span = createPowerStatsSpan(stats);
+ if (span != null) {
+ span.dump(ipw);
+ }
+ });
+ }
+
+ @Override
+ public long storeEstimatedPowerConsumption(BatteryStatsHistory batteryStatsHistory,
+ long startTime, long endTimeMs) {
+ long[] lastSavedMonotonicTime = new long[1];
+ mPowerStatsAggregator.aggregatePowerStats(batteryStatsHistory, startTime, endTimeMs,
+ stats -> {
+ storeAggregatedPowerStats(stats);
+ lastSavedMonotonicTime[0] = stats.getStartTime() + stats.getDuration();
+ });
+ return lastSavedMonotonicTime[0];
+ }
+
+ @VisibleForTesting
+ void storeAggregatedPowerStats(AggregatedPowerStats stats) {
+ PowerStatsSpan span = createPowerStatsSpan(stats);
+ if (span == null) {
+ return;
+ }
+ mPowerStatsStore.storePowerStatsSpan(span);
+ }
+
+ private static PowerStatsSpan createPowerStatsSpan(AggregatedPowerStats stats) {
+ List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
+ if (clockUpdates.isEmpty()) {
+ Slog.w(TAG, "No clock updates in aggregated power stats " + stats);
+ return null;
+ }
+
+ long monotonicTime = clockUpdates.get(0).monotonicTime;
+ long durationSum = 0;
+ PowerStatsSpan span = new PowerStatsSpan(monotonicTime);
+ for (int i = 0; i < clockUpdates.size(); i++) {
+ AggregatedPowerStats.ClockUpdate clockUpdate = clockUpdates.get(i);
+ long duration;
+ if (i == clockUpdates.size() - 1) {
+ duration = stats.getDuration() - durationSum;
+ } else {
+ duration = clockUpdate.monotonicTime - monotonicTime;
+ }
+ span.addTimeFrame(clockUpdate.monotonicTime, clockUpdate.currentTime, duration);
+ monotonicTime = clockUpdate.monotonicTime;
+ durationSum += duration;
+ }
+
+ span.addSection(new AggregatedPowerStatsSection(stats));
+ return span;
+ }
+
+ @Override
+ public long getLastSavedEstimatesPowerConsumptionTimestamp() {
+ long timestamp = -1;
+ for (PowerStatsSpan.Metadata metadata : mPowerStatsStore.getTableOfContents()) {
+ if (metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
+ for (PowerStatsSpan.TimeFrame timeFrame : metadata.getTimeFrames()) {
+ long endMonotonicTime = timeFrame.startMonotonicTime + timeFrame.duration;
+ if (endMonotonicTime > timestamp) {
+ timestamp = endMonotonicTime;
+ }
+ }
+ }
+ }
+ return timestamp;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
similarity index 94%
rename from services/core/java/com/android/server/power/stats/MultiStateStats.java
rename to services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
index c3a0aeb..28474a5 100644
--- a/services/core/java/com/android/server/power/stats/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.util.Slog;
@@ -37,7 +37,7 @@
* CPU residency, Network packet counts etc. All metrics must be represented as <code>long</code>
* values;
*/
-public class MultiStateStats {
+class MultiStateStats {
private static final String TAG = "MultiStateStats";
private static final String XML_TAG_STATS = "stats";
@@ -47,12 +47,12 @@
* A set of states, e.g. on-battery, screen-on, procstate. The state values are integers
* from 0 to States.mLabels.length
*/
- public static class States {
+ static class States {
final String mName;
final boolean mTracked;
final String[] mLabels;
- public States(String name, boolean tracked, String... labels) {
+ States(String name, boolean tracked, String... labels) {
mName = name;
mTracked = tracked;
mLabels = labels;
@@ -121,7 +121,7 @@
* Factory for MultiStateStats containers. All generated containers retain their connection
* to the Factory and the corresponding configuration.
*/
- public static class Factory {
+ static class Factory {
private static final int INVALID_SERIAL_STATE = -1;
final int mDimensionCount;
final States[] mStates;
@@ -147,7 +147,7 @@
final int[] mCompositeToSerialState;
final int mSerialStateCount;
- public Factory(int dimensionCount, States... states) {
+ Factory(int dimensionCount, States... states) {
mDimensionCount = dimensionCount;
mStates = states;
@@ -250,7 +250,7 @@
/**
* Allocates a new stats container using this Factory's configuration.
*/
- public MultiStateStats create() {
+ MultiStateStats create() {
return new MultiStateStats(this, mDimensionCount);
}
@@ -293,16 +293,16 @@
private int mCompositeState;
private boolean mTracking;
- public MultiStateStats(Factory factory, int dimensionCount) {
+ MultiStateStats(Factory factory, int dimensionCount) {
this.mFactory = factory;
mCounter = new LongArrayMultiStateCounter(factory.mSerialStateCount, dimensionCount);
}
- public int getDimensionCount() {
+ int getDimensionCount() {
return mFactory.mDimensionCount;
}
- public States[] getStates() {
+ States[] getStates() {
return mFactory.mStates;
}
@@ -310,7 +310,7 @@
* Copies time-in-state and timestamps from the supplied prototype. Does not
* copy accumulated counts.
*/
- public void copyStatesFrom(MultiStateStats otherStats) {
+ void copyStatesFrom(MultiStateStats otherStats) {
mCounter.copyStatesFrom(otherStats.mCounter);
}
@@ -322,7 +322,7 @@
* @param state The new value of the state (e.g. 0 or 1 for "on-battery")
* @param timestampMs The time when the state change occurred
*/
- public void setState(int stateIndex, int state, long timestampMs) {
+ void setState(int stateIndex, int state, long timestampMs) {
if (!mTracking) {
mCounter.updateValues(new long[mCounter.getArrayLength()], timestampMs);
mTracking = true;
@@ -335,7 +335,7 @@
* Adds the delta to the metrics. The number of values must correspond to the dimension count
* supplied to the Factory constructor
*/
- public void increment(long[] values, long timestampMs) {
+ void increment(long[] values, long timestampMs) {
mCounter.incrementValues(values, timestampMs);
mTracking = true;
}
@@ -343,21 +343,21 @@
/**
* Returns accumulated stats for the specified composite state.
*/
- public void getStats(long[] outValues, int[] states) {
+ void getStats(long[] outValues, int[] states) {
mCounter.getCounts(outValues, mFactory.getSerialState(states));
}
/**
* Updates the stats values for the provided combination of states.
*/
- public void setStats(int[] states, long[] values) {
+ void setStats(int[] states, long[] values) {
mCounter.setValues(mFactory.getSerialState(states), values);
}
/**
* Resets the counters.
*/
- public void reset() {
+ void reset() {
mCounter.reset();
mTracking = false;
}
@@ -365,7 +365,7 @@
/**
* Stores contents in an XML doc.
*/
- public void writeXml(TypedXmlSerializer serializer) throws IOException {
+ void writeXml(TypedXmlSerializer serializer) throws IOException {
long[] tmpArray = new long[mCounter.getArrayLength()];
try {
@@ -420,8 +420,7 @@
* Populates the object with contents in an XML doc. The parser is expected to be
* positioned on the opening tag of the corresponding element.
*/
- public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
- IOException {
+ boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
String outerTag = parser.getName();
long[] tmpArray = new long[mCounter.getArrayLength()];
int eventType = parser.getEventType();
diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
similarity index 88%
rename from services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
index ec23fa0..3957ae0 100644
--- a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
@@ -13,24 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
+import com.android.server.power.stats.format.PhoneCallPowerStatsLayout;
-public class PhoneCallPowerStatsProcessor extends PowerStatsProcessor {
- private final PowerStatsLayout mStatsLayout;
+class PhoneCallPowerStatsProcessor extends PowerStatsProcessor {
+ private final PhoneCallPowerStatsLayout mStatsLayout;
private final PowerStats.Descriptor mDescriptor;
private final long[] mTmpDeviceStats;
private PowerStats.Descriptor mMobileRadioStatsDescriptor;
private MobileRadioPowerStatsLayout mMobileRadioStatsLayout;
private long[] mTmpMobileRadioDeviceStats;
- public PhoneCallPowerStatsProcessor() {
- mStatsLayout = new PowerStatsLayout();
- mStatsLayout.addDeviceSectionPowerEstimate();
+ PhoneCallPowerStatsProcessor() {
+ mStatsLayout = new PhoneCallPowerStatsLayout();
PersistableBundle extras = new PersistableBundle();
mStatsLayout.toExtras(extras);
mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_PHONE,
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
similarity index 98%
rename from services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
rename to services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
index a92a6fd3..d04c5ba 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
-
-import static com.android.server.power.stats.MultiStateStats.STATE_DOES_NOT_EXIST;
+package com.android.server.power.stats.processor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -147,7 +145,8 @@
int uidStateId = MultiStateStats.States
.findTrackedStateByName(mUidStateConfig, mDeviceStateConfig[stateId].getName());
- if (uidStateId != STATE_DOES_NOT_EXIST && mUidStateConfig[uidStateId].isTracked()) {
+ if (uidStateId != MultiStateStats.STATE_DOES_NOT_EXIST
+ && mUidStateConfig[uidStateId].isTracked()) {
for (int i = mUidStats.size() - 1; i >= 0; i--) {
PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
if (uidStats.stats == null) {
@@ -271,7 +270,7 @@
if (mUidStateConfig[stateId].isTracked()) {
int deviceStateId = MultiStateStats.States.findTrackedStateByName(
mDeviceStateConfig, mUidStateConfig[stateId].getName());
- if (deviceStateId != STATE_DOES_NOT_EXIST
+ if (deviceStateId != MultiStateStats.STATE_DOES_NOT_EXIST
&& mDeviceStateConfig[deviceStateId].isTracked()) {
uidStats.states[stateId] = mDeviceStates[deviceStateId];
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
similarity index 92%
rename from services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
rename to services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
index c734f68..32c1056 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.annotation.NonNull;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.util.SparseBooleanArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
@@ -33,20 +34,22 @@
public class PowerStatsAggregator {
private static final long UNINITIALIZED = -1;
private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
- private final BatteryStatsHistory mHistory;
private final SparseBooleanArray mEnabledComponents =
new SparseBooleanArray(BatteryConsumer.POWER_COMPONENT_COUNT + 10);
private AggregatedPowerStats mStats;
private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
- public PowerStatsAggregator(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
- @NonNull BatteryStatsHistory history) {
- mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
- mHistory = history;
+ @VisibleForTesting
+ public PowerStatsAggregator() {
+ this(new AggregatedPowerStatsConfig());
}
- public AggregatedPowerStatsConfig getConfig() {
+ PowerStatsAggregator(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
+ mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
+ }
+
+ AggregatedPowerStatsConfig getConfig() {
return mAggregatedPowerStatsConfig;
}
@@ -71,7 +74,7 @@
* Note: the AggregatedPowerStats object is reused, so the consumer should fully consume
* the stats in the <code>accept</code> method and never cache it.
*/
- public void aggregatePowerStats(long startTimeMs, long endTimeMs,
+ public void aggregatePowerStats(BatteryStatsHistory history, long startTimeMs, long endTimeMs,
Consumer<AggregatedPowerStats> consumer) {
synchronized (this) {
if (mStats == null) {
@@ -85,7 +88,7 @@
long lastTime = 0;
int lastStates = 0xFFFFFFFF;
int lastStates2 = 0xFFFFFFFF;
- try (BatteryStatsHistoryIterator iterator = mHistory.iterate(startTimeMs, endTimeMs)) {
+ try (BatteryStatsHistoryIterator iterator = history.iterate(startTimeMs, endTimeMs)) {
while (iterator.hasNext()) {
BatteryStats.HistoryItem item = iterator.next();
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
similarity index 94%
rename from services/core/java/com/android/server/power/stats/PowerStatsExporter.java
rename to services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
index c5bed24..fab87d6 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.annotation.Nullable;
import android.os.AggregateBatteryConsumer;
@@ -24,7 +24,11 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.PowerStatsSpan;
+import com.android.server.power.stats.PowerStatsStore;
+import com.android.server.power.stats.format.PowerStatsLayout;
import java.util.ArrayList;
import java.util.Arrays;
@@ -35,19 +39,18 @@
* Given a time range, converts accumulated PowerStats to BatteryUsageStats. Combines
* stores spans of PowerStats with the yet-unprocessed tail of battery history.
*/
-public class PowerStatsExporter {
+class PowerStatsExporter {
private static final String TAG = "PowerStatsExporter";
private final PowerStatsStore mPowerStatsStore;
private final PowerStatsAggregator mPowerStatsAggregator;
private final long mBatterySessionTimeSpanSlackMillis;
private static final long BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS = TimeUnit.MINUTES.toMillis(2);
- public PowerStatsExporter(PowerStatsStore powerStatsStore,
- PowerStatsAggregator powerStatsAggregator) {
+ PowerStatsExporter(PowerStatsStore powerStatsStore, PowerStatsAggregator powerStatsAggregator) {
this(powerStatsStore, powerStatsAggregator, BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS);
}
- public PowerStatsExporter(PowerStatsStore powerStatsStore,
+ PowerStatsExporter(PowerStatsStore powerStatsStore,
PowerStatsAggregator powerStatsAggregator,
long batterySessionTimeSpanSlackMillis) {
mPowerStatsStore = powerStatsStore;
@@ -59,8 +62,8 @@
* Populates the provided BatteryUsageStats.Builder with power estimates from the accumulated
* PowerStats, both stored in PowerStatsStore and not-yet processed.
*/
- public void exportAggregatedPowerStats(BatteryUsageStats.Builder batteryUsageStatsBuilder,
- long monotonicStartTime, long monotonicEndTime) {
+ void exportAggregatedPowerStats(BatteryUsageStats.Builder batteryUsageStatsBuilder,
+ BatteryStatsHistory history, long monotonicStartTime, long monotonicEndTime) {
synchronized (mPowerStatsAggregator) {
boolean hasStoredSpans = false;
long maxEndTime = monotonicStartTime;
@@ -111,7 +114,7 @@
if (!hasStoredSpans
|| maxEndTime < monotonicEndTime - mBatterySessionTimeSpanSlackMillis) {
- mPowerStatsAggregator.aggregatePowerStats(maxEndTime, monotonicEndTime,
+ mPowerStatsAggregator.aggregatePowerStats(history, maxEndTime, monotonicEndTime,
stats -> populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats));
}
mPowerStatsAggregator.reset();
@@ -140,9 +143,7 @@
return;
}
- PowerStatsLayout layout = new PowerStatsLayout();
- layout.fromExtras(descriptor.extras);
-
+ PowerStatsLayout layout = new PowerStatsLayout(descriptor);
long[] deviceStats = new long[descriptor.statsArrayLength];
for (int screenState = 0; screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) {
if (batteryUsageStatsBuilder.isScreenStateDataNeeded()) {
@@ -328,8 +329,8 @@
BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, allAppsScope,
powerComponentId, screenState, powerState);
if (key != null) {
- allAppsScope.addConsumedPower(key, powerAllApps,
- BatteryConsumer.POWER_MODEL_UNDEFINED);
+ allAppsScope.addConsumedPower(key, powerAllApps,
+ BatteryConsumer.POWER_MODEL_UNDEFINED);
}
allAppsScope.addConsumedPower(powerComponentId, powerAllApps,
BatteryConsumer.POWER_MODEL_UNDEFINED);
@@ -393,7 +394,7 @@
return true;
}
- void setPowerComponentEnabled(int powerComponentId, boolean enabled) {
+ public void setPowerComponentEnabled(int powerComponentId, boolean enabled) {
mPowerStatsAggregator.setPowerComponentEnabled(powerComponentId, enabled);
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsProcessor.java
similarity index 97%
rename from services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/PowerStatsProcessor.java
index 6a8c6b12..838fc62 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsProcessor.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
-import static com.android.server.power.stats.MultiStateStats.STATE_DOES_NOT_EXIST;
-import static com.android.server.power.stats.MultiStateStats.States.findTrackedStateByName;
+import static com.android.server.power.stats.processor.MultiStateStats.STATE_DOES_NOT_EXIST;
+import static com.android.server.power.stats.processor.MultiStateStats.States.findTrackedStateByName;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -43,7 +43,7 @@
* 2. For each UID, compute the proportion of the combined estimates in each state
* and attribute the corresponding portion of the total power estimate in that state to the UID.
*/
-public abstract class PowerStatsProcessor {
+abstract class PowerStatsProcessor {
private static final String TAG = "PowerStatsProcessor";
private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java
similarity index 93%
rename from services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java
index 8fb1fd6..b295e30 100644
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java
@@ -14,28 +14,30 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import android.os.BatteryStats;
import android.util.Slog;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
import java.util.ArrayList;
import java.util.List;
-public class ScreenPowerStatsProcessor extends PowerStatsProcessor {
+class ScreenPowerStatsProcessor extends PowerStatsProcessor {
private static final String TAG = "ScreenPowerStatsProcessor";
private final int mDisplayCount;
private final UsageBasedPowerEstimator[] mScreenOnPowerEstimators;
@@ -51,7 +53,7 @@
public double power;
}
- public ScreenPowerStatsProcessor(PowerProfile powerProfile) {
+ ScreenPowerStatsProcessor(PowerProfile powerProfile) {
mDisplayCount = powerProfile.getNumDisplays();
mScreenOnPowerEstimators = new UsageBasedPowerEstimator[mDisplayCount];
mScreenDozePowerEstimators = new UsageBasedPowerEstimator[mDisplayCount];
diff --git a/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
similarity index 90%
rename from services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
index 79d8076..67013ea 100644
--- a/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.hardware.Sensor;
import android.hardware.SensorManager;
@@ -25,14 +25,16 @@
import android.util.SparseArray;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.PowerStatsLayout;
+import com.android.server.power.stats.format.SensorPowerStatsLayout;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.List;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
-public class SensorPowerStatsProcessor extends PowerStatsProcessor {
+class SensorPowerStatsProcessor extends PowerStatsProcessor {
private static final String TAG = "SensorPowerStatsProcessor";
private static final String ANDROID_SENSOR_TYPE_PREFIX = "android.sensor.";
@@ -64,7 +66,7 @@
private long[] mTmpDeviceStatsArray;
private long[] mTmpUidStatsArray;
- public SensorPowerStatsProcessor(Supplier<SensorManager> sensorManagerSupplier) {
+ SensorPowerStatsProcessor(Supplier<SensorManager> sensorManagerSupplier) {
mSensorManagerSupplier = sensorManagerSupplier;
}
@@ -78,16 +80,9 @@
return false;
}
- mStatsLayout = new SensorPowerStatsLayout();
- List<Sensor> sensorList = new ArrayList<>(mSensorManager.getSensorList(Sensor.TYPE_ALL));
- sensorList.sort(Comparator.comparingInt(Sensor::getId));
- for (int i = 0; i < sensorList.size(); i++) {
- Sensor sensor = sensorList.get(i);
- String label = makeLabel(sensor, sensorList);
- mStatsLayout.addUidSensorSection(sensor.getHandle(), label);
- }
- mStatsLayout.addUidSectionPowerEstimate();
- mStatsLayout.addDeviceSectionPowerEstimate();
+ List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ mStatsLayout = new SensorPowerStatsLayout(sensorList.stream().collect(
+ Collectors.toMap(Sensor::getHandle, sensor -> makeLabel(sensor, sensorList))));
PersistableBundle extras = new PersistableBundle();
mStatsLayout.toExtras(extras);
@@ -231,7 +226,8 @@
sensorState.startTime = time;
}
- private void flushPowerStats(PowerComponentAggregatedPowerStats stats, long timestamp) {
+ private void flushPowerStats(
+ PowerComponentAggregatedPowerStats stats, long timestamp) {
mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
stats.addProcessedPowerStats(mPowerStats, timestamp);
@@ -240,7 +236,8 @@
mLastUpdateTimestamp = timestamp;
}
- private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+ private void computeUidPowerEstimates(
+ PowerComponentAggregatedPowerStats stats,
List<Integer> uids) {
List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
int[] uidSensorDurationPositions = new int[sensorList.size()];
@@ -292,7 +289,8 @@
}
}
- private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+ private void computeDevicePowerEstimates(
+ PowerComponentAggregatedPowerStats stats) {
for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
CombinedDeviceStateEstimate estimation =
mPlan.combinedDeviceStateEstimations.get(i);
diff --git a/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java
similarity index 80%
rename from services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java
index 48dac8a..a6c3807 100644
--- a/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerStatsUidResolver;
-public class VideoPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
- public VideoPowerStatsProcessor(PowerProfile powerProfile,
- PowerStatsUidResolver uidResolver) {
+class VideoPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ VideoPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
super(BatteryConsumer.POWER_COMPONENT_VIDEO, uidResolver,
powerProfile.getAveragePower(PowerProfile.POWER_VIDEO));
}
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java
similarity index 98%
rename from services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java
index 4e035c3..0df01cf 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import android.util.Slog;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.WifiPowerStatsLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class WifiPowerStatsProcessor extends PowerStatsProcessor {
+class WifiPowerStatsProcessor extends PowerStatsProcessor {
private static final String TAG = "WifiPowerStatsProcessor";
private static final boolean DEBUG = false;
@@ -46,7 +48,7 @@
private long[] mTmpUidStatsArray;
private boolean mHasWifiPowerController;
- public WifiPowerStatsProcessor(PowerProfile powerProfile) {
+ WifiPowerStatsProcessor(PowerProfile powerProfile) {
mRxPowerEstimator = new UsageBasedPowerEstimator(
powerProfile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX));
mTxPowerEstimator = new UsageBasedPowerEstimator(
diff --git a/services/core/java/com/android/server/rollback/ApexdRevertLogger.java b/services/core/java/com/android/server/rollback/ApexdRevertLogger.java
new file mode 100644
index 0000000..9950cc7
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/ApexdRevertLogger.java
@@ -0,0 +1,167 @@
+/*
+ * 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.server.rollback;
+
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.os.SystemProperties;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class handles the logic for logging Apexd-triggered rollback events.
+ * TODO: b/354112511 Refactor to have a separate metric for ApexdReverts
+ */
+public final class ApexdRevertLogger {
+ private static final String TAG = "WatchdogRollbackLogger";
+
+ private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+
+ /**
+ * Logs that one or more apexd reverts have occurred, along with the crashing native process
+ * that caused apexd to revert during boot.
+ *
+ * @param context the context to use when determining the log packages
+ * @param failedPackageNames a list of names of packages which were reverted
+ * @param failingNativeProcess the crashing native process which caused a revert
+ */
+ public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames,
+ @NonNull String failingNativeProcess) {
+ Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames);
+ for (VersionedPackage logPackage: logPackages) {
+ logEvent(logPackage,
+ failingNativeProcess);
+ }
+ }
+
+ /**
+ * Gets the set of parent packages for a given set of failed package names. In the case that
+ * multiple sessions have failed, we want to log failure for each of the parent packages.
+ * Even if multiple failed packages have the same parent, we only log the parent package once.
+ */
+ private static Set<VersionedPackage> getLogPackages(Context context,
+ @NonNull List<String> failedPackageNames) {
+ Set<VersionedPackage> parentPackages = new ArraySet<>();
+ for (String failedPackageName: failedPackageNames) {
+ parentPackages.add(getLogPackage(context, new VersionedPackage(failedPackageName, 0)));
+ }
+ return parentPackages;
+ }
+
+ /**
+ * Returns the logging parent of a given package if it exists, {@code null} otherwise.
+ *
+ * The logging parent is defined by the {@code android.content.pm.LOGGING_PARENT} field in the
+ * metadata of a package's AndroidManifest.xml.
+ */
+ @VisibleForTesting
+ @Nullable
+ private static VersionedPackage getLogPackage(Context context,
+ @NonNull VersionedPackage failingPackage) {
+ String logPackageName;
+ VersionedPackage loggingParent;
+ logPackageName = getLoggingParentName(context, failingPackage.getPackageName());
+ if (logPackageName == null) {
+ return null;
+ }
+ try {
+ loggingParent = new VersionedPackage(logPackageName, context.getPackageManager()
+ .getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode());
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ return loggingParent;
+ }
+
+ @Nullable
+ private static String getLoggingParentName(Context context, @NonNull String packageName) {
+ PackageManager packageManager = context.getPackageManager();
+ try {
+ int flags = PackageManager.MATCH_APEX | PackageManager.GET_META_DATA;
+ ApplicationInfo ai = packageManager.getPackageInfo(packageName, flags).applicationInfo;
+ if (ai == null || ai.metaData == null) {
+ return null;
+ }
+ return ai.metaData.getString(LOGGING_PARENT_KEY);
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to discover logging parent package: " + packageName, e);
+ return null;
+ }
+ }
+
+ /**
+ * Log a Apexd rollback event to statsd.
+ *
+ * @param logPackage the package to associate the rollback with.
+ * @param failingPackageName the failing package or process which triggered the rollback.
+ */
+ private static void logEvent(@Nullable VersionedPackage logPackage,
+ @NonNull String failingPackageName) {
+ Slog.i(TAG, "Watchdog event occurred with type: ROLLBACK_SUCCESS"
+ + " logPackage: " + logPackage
+ + " rollbackReason: REASON_NATIVE_CRASH_DURING_BOOT"
+ + " failedPackageName: " + failingPackageName);
+ CrashRecoveryStatsLog.write(
+ WATCHDOG_ROLLBACK_OCCURRED,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ (logPackage != null) ? logPackage.getPackageName() : "",
+ (logPackage != null) ? logPackage.getVersionCode() : 0,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT,
+ failingPackageName,
+ new byte[]{});
+
+ logTestProperties(logPackage, failingPackageName);
+ }
+
+ /**
+ * Writes properties which will be used by rollback tests to check if rollback has occurred
+ * have occurred.
+ *
+ * persist.sys.rollbacktest.enabled: true if rollback tests are running
+ * persist.sys.rollbacktest.ROLLBACK_SUCCESS.logPackage: the package to associate the rollback
+ * persist.sys.rollbacktest.ROLLBACK_SUCCESS.rollbackReason: the reason Apexd triggered it
+ * persist.sys.rollbacktest.ROLLBACK_SUCCESS.failedPackageName: the failing package or process
+ * which triggered the rollback
+ */
+ private static void logTestProperties(@Nullable VersionedPackage logPackage,
+ @NonNull String failingPackageName) {
+ // This property should be on only during the tests
+ final String prefix = "persist.sys.rollbacktest.";
+ if (!SystemProperties.getBoolean(prefix + "enabled", false)) {
+ return;
+ }
+ String key = prefix + "ROLLBACK_SUCCESS";
+ SystemProperties.set(key, String.valueOf(true));
+ SystemProperties.set(key + ".logPackage", logPackage != null ? logPackage.toString() : "");
+ SystemProperties.set(key + ".rollbackReason", "REASON_NATIVE_CRASH_DURING_BOOT");
+ SystemProperties.set(key + ".failedPackageName", failingPackageName);
+ }
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 1c786e6..68026ea 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -18,6 +18,8 @@
import static android.content.pm.Flags.provideInfoOfApkInApex;
+import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
+
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -40,6 +42,7 @@
import android.os.SystemProperties;
import android.sysprop.CrashRecoveryProperties;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -532,11 +535,13 @@
private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
@FailureReasons int rollbackReason) {
assertInWorkerThread();
+ String failedPackageName = (failedPackage == null ? null : failedPackage.getPackageName());
Slog.i(TAG, "Rolling back package. RollbackId: " + rollback.getRollbackId()
- + " failedPackage: "
- + (failedPackage == null ? null : failedPackage.getPackageName())
+ + " failedPackage: " + failedPackageName
+ " rollbackReason: " + rollbackReason);
+ logCrashRecoveryEvent(Log.DEBUG, String.format("Rolling back %s. Reason: %s",
+ failedPackageName, rollbackReason));
final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason);
final String failedPackageToLog;
@@ -724,6 +729,7 @@
}
Slog.i(TAG, "Rolling back all available low impact rollbacks");
+ logCrashRecoveryEvent(Log.DEBUG, "Rolling back all available. Reason: " + rollbackReason);
// Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all
// pending staged rollbacks are handled.
for (RollbackInfo rollback : lowImpactRollbacks) {
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 7fc0292..79560ce 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -16,6 +16,7 @@
package com.android.server.rollback;
+import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING;
@@ -39,7 +40,7 @@
import android.content.rollback.RollbackInfo;
import android.os.SystemProperties;
import android.text.TextUtils;
-import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -47,7 +48,6 @@
import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
import java.util.List;
-import java.util.Set;
/**
* This class handles the logic for logging Watchdog-triggered rollback events.
@@ -101,22 +101,6 @@
return loggingParent;
}
-
- /**
- * Gets the set of parent packages for a given set of failed package names. In the case that
- * multiple sessions have failed, we want to log failure for each of the parent packages.
- * Even if multiple failed packages have the same parent, we only log the parent package once.
- */
- private static Set<VersionedPackage> getLogPackages(Context context,
- @NonNull List<String> failedPackageNames) {
- Set<VersionedPackage> parentPackages = new ArraySet<>();
- for (String failedPackageName: failedPackageNames) {
- parentPackages.add(getLogPackage(context, new VersionedPackage(failedPackageName, 0)));
- }
- return parentPackages;
- }
-
-
static void logRollbackStatusOnBoot(Context context, int rollbackId, String logPackageName,
List<RollbackInfo> recentlyCommittedRollbacks) {
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
@@ -165,25 +149,6 @@
}
/**
- * Logs that one or more apexd reverts have occurred, along with the crashing native process
- * that caused apexd to revert during boot.
- *
- * @param context the context to use when determining the log packages
- * @param failedPackageNames a list of names of packages which were reverted
- * @param failingNativeProcess the crashing native process which caused a revert
- */
- public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames,
- @NonNull String failingNativeProcess) {
- Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames);
- for (VersionedPackage logPackage: logPackages) {
- logEvent(logPackage,
- WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
- WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT,
- failingNativeProcess);
- }
- }
-
- /**
* Log a Watchdog rollback event to statsd.
*
* @param logPackage the package to associate the rollback with.
@@ -193,10 +158,11 @@
*/
public static void logEvent(@Nullable VersionedPackage logPackage, int type,
int rollbackReason, @NonNull String failingPackageName) {
- Slog.i(TAG, "Watchdog event occurred with type: " + rollbackTypeToString(type)
+ String logMsg = "Watchdog event occurred with type: " + rollbackTypeToString(type)
+ " logPackage: " + logPackage
+ " rollbackReason: " + rollbackReasonToString(rollbackReason)
- + " failedPackageName: " + failingPackageName);
+ + " failedPackageName: " + failingPackageName;
+ Slog.i(TAG, logMsg);
if (logPackage != null) {
CrashRecoveryStatsLog.write(
CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
@@ -219,33 +185,19 @@
new byte[]{});
}
- logTestProperties(logPackage, type, rollbackReason, failingPackageName);
+ logTestProperties(logMsg);
}
/**
* Writes properties which will be used by rollback tests to check if particular rollback
* events have occurred.
- *
- * persist.sys.rollbacktest.enabled: true if rollback tests are running
- * persist.sys.rollbacktest.EVENT_TYPE: true if a particular rollback event has occurred
- * ex: persist.sys.rollbacktest.ROLLBACK_INITIATE is true if ROLLBACK_INITIATE has happened
- * persist.sys.rollbacktest.EVENT_TYPE.logPackage: the package to associate the rollback with
- * persist.sys.rollbacktest.EVENT_TYPE.rollbackReason: the reason Watchdog triggered a rollback
- * persist.sys.rollbacktest.EVENT_TYPE.failedPackageName: the failing package or process which
- * triggered the rollback
*/
- private static void logTestProperties(@Nullable VersionedPackage logPackage, int type,
- int rollbackReason, @NonNull String failingPackageName) {
+ private static void logTestProperties(String logMsg) {
// This property should be on only during the tests
- final String prefix = "persist.sys.rollbacktest.";
- if (!SystemProperties.getBoolean(prefix + "enabled", false)) {
+ if (!SystemProperties.getBoolean("persist.sys.rollbacktest.enabled", false)) {
return;
}
- String key = prefix + rollbackTypeToString(type);
- SystemProperties.set(key, String.valueOf(true));
- SystemProperties.set(key + ".logPackage", logPackage != null ? logPackage.toString() : "");
- SystemProperties.set(key + ".rollbackReason", rollbackReasonToString(rollbackReason));
- SystemProperties.set(key + ".failedPackageName", failingPackageName);
+ logCrashRecoveryEvent(Log.DEBUG, logMsg);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 09d2a02..83cb72e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -35,7 +35,8 @@
public interface StatusBarManagerInternal {
void setNotificationDelegate(NotificationDelegate delegate);
- void showScreenPinningRequest(int taskId);
+ /** Show a screen pinning request for a specific task. */
+ void showScreenPinningRequest(int taskId, int userId);
void showAssistDisclosure();
void preloadRecentApps();
@@ -136,7 +137,7 @@
*
* @param hidesStatusBar whether it is being hidden
*/
- void setTopAppHidesStatusBar(boolean hidesStatusBar);
+ void setTopAppHidesStatusBar(int displayId, boolean hidesStatusBar);
boolean showShutdownUi(boolean isReboot, String requestString);
@@ -149,17 +150,18 @@
/**
* Notify System UI that the system get into or exit immersive mode.
+ * @param displayId The changed display Id.
* @param rootDisplayAreaId The changed display area Id.
* @param isImmersiveMode {@code true} if the display area get into immersive mode.
*/
- void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode);
+ void immersiveModeChanged(int displayId, int rootDisplayAreaId, boolean isImmersiveMode);
/**
* Show a rotation suggestion that a user may approve to rotate the screen.
*
* @param rotation rotation suggestion
*/
- void onProposedRotationChanged(int rotation, boolean isValid);
+ void onProposedRotationChanged(int displayId, int rotation, boolean isValid);
/**
* Notifies System UI that the display is ready to show system decorations.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 0fd5967..908f51b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -119,6 +119,7 @@
import com.android.server.UiThread;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.notification.NotificationDelegate;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.GlobalActionsProvider;
import com.android.server.power.ShutdownCheckPoints;
@@ -185,6 +186,7 @@
private final ActivityManagerInternal mActivityManagerInternal;
private final ActivityTaskManagerInternal mActivityTaskManager;
private final PackageManagerInternal mPackageManagerInternal;
+ private final UserManagerInternal mUserManagerInternal;
private final SessionMonitor mSessionMonitor;
private int mCurrentUserId;
private boolean mTracingEnabled;
@@ -304,6 +306,7 @@
mActivityTaskManager = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mTileRequestTracker = new TileRequestTracker(mContext);
mSessionMonitor = new SessionMonitor(mContext);
@@ -360,7 +363,14 @@
}
@Override
- public void showScreenPinningRequest(int taskId) {
+ public void showScreenPinningRequest(int taskId, int userId) {
+ if (isVisibleBackgroundUser(userId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping showScreenPinningRequest for visible background user "
+ + userId);
+ }
+ return;
+ }
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -439,6 +449,13 @@
@Override
public void appTransitionFinished(int displayId) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping appTransitionFinished for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
enforceStatusBarService();
IStatusBar bar = mBar;
if (bar != null) {
@@ -588,6 +605,13 @@
@Override
public void setWindowState(int displayId, int window, int state) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping setWindowState for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -598,6 +622,13 @@
@Override
public void appTransitionPending(int displayId) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping appTransitionPending for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -608,6 +639,13 @@
@Override
public void appTransitionCancelled(int displayId) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping appTransitionCancelled for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -619,6 +657,13 @@
@Override
public void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
long statusBarAnimationsDuration) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping appTransitionStarting for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -629,7 +674,14 @@
}
@Override
- public void setTopAppHidesStatusBar(boolean hidesStatusBar) {
+ public void setTopAppHidesStatusBar(int displayId, boolean hidesStatusBar) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping setTopAppHidesStatusBar for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -665,10 +717,18 @@
}
@Override
- public void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {
+ public void immersiveModeChanged(int displayId, int rootDisplayAreaId,
+ boolean isImmersiveMode) {
if (mBar == null) {
return;
}
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping immersiveModeChanged for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
if (!CLIENT_TRANSIENT) {
// Only call from here when the client transient is not enabled.
try {
@@ -680,7 +740,14 @@
// TODO(b/118592525): support it per display if necessary.
@Override
- public void onProposedRotationChanged(int rotation, boolean isValid) {
+ public void onProposedRotationChanged(int displayId, int rotation, boolean isValid) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping onProposedRotationChanged for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
if (mBar != null){
try {
mBar.onProposedRotationChanged(rotation, isValid);
@@ -690,6 +757,13 @@
@Override
public void onDisplayReady(int displayId) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping onDisplayReady for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -703,6 +777,13 @@
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
@Behavior int behavior, @InsetsType int requestedVisibleTypes,
String packageName, LetterboxDetails[] letterboxDetails) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping onSystemBarAttributesChanged for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
getUiState(displayId).setBarAttributes(appearance, appearanceRegions,
navbarColorManagedByIme, behavior, requestedVisibleTypes, packageName,
letterboxDetails);
@@ -719,6 +800,13 @@
@Override
public void showTransient(int displayId, @InsetsType int types,
boolean isGestureOnSystemBar) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping showTransient for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
getUiState(displayId).showTransient(types);
IStatusBar bar = mBar;
if (bar != null) {
@@ -730,6 +818,13 @@
@Override
public void abortTransient(int displayId, @InsetsType int types) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping abortTransient for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
getUiState(displayId).clearTransient(types);
IStatusBar bar = mBar;
if (bar != null) {
@@ -776,6 +871,15 @@
@Override
public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG,
+ "Skipping setNavigationBarLumaSamplingEnabled for visible background "
+ + "user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -1416,6 +1520,13 @@
}
private void setDisableFlags(int displayId, int flags, String cause) {
+ if (isVisibleBackgroundUserOnDisplay(displayId)) {
+ if (SPEW) {
+ Slog.d(TAG, "Skipping setDisableFlags for visible background user "
+ + mUserManagerInternal.getUserAssignedToDisplay(displayId));
+ }
+ return;
+ }
// also allows calls from window manager which is in this process.
enforceStatusBarService();
@@ -2713,16 +2824,30 @@
if (callingUserId == USER_SYSTEM || callingUserId == mCurrentUserId) {
return;
}
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mUserManager.isSameProfileGroup(callingUserId, mCurrentUserId)) {
- return;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
+ if (!isVisibleBackgroundUser(callingUserId)) {
+ return;
}
throw new SecurityException("User " + callingUserId
+ " is not permitted to use this method");
}
-}
+
+ private boolean isVisibleBackgroundUser(int userId) {
+ if (!mVisibleBackgroundUsersEnabled) {
+ return false;
+ }
+ // The main use case for visible background users is the Automotive multi-display
+ // configuration where a passenger can use a secondary display while the driver is
+ // using the main display.
+ // TODO(b/341604160) - Support visible background users properly and remove carve outs
+ return mUserManagerInternal.isVisibleBackgroundFullUser(userId);
+ }
+
+ private boolean isVisibleBackgroundUserOnDisplay(int displayId) {
+ if (!mVisibleBackgroundUsersEnabled) {
+ return false;
+ }
+ int userId = mUserManagerInternal.getUserAssignedToDisplay(displayId);
+ return isVisibleBackgroundUser(userId);
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6b3b5bd..91a17a9 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -251,7 +251,7 @@
}
private void registerBroadcastReceivers() {
- PackageMonitor monitor = new PackageMonitor() {
+ PackageMonitor monitor = new PackageMonitor(/* supportsPackageRestartQuery */ true) {
private void buildTvInputList(String[] packages) {
int userId = getChangingUserId();
synchronized (mLock) {
@@ -599,13 +599,6 @@
ComponentName component = it.next();
ServiceState serviceState = userState.serviceStateMap.get(component);
if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
- if (serviceState.callback != null) {
- try {
- serviceState.service.unregisterCallback(serviceState.callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in unregisterCallback", e);
- }
- }
unbindService(serviceState);
it.remove();
}
@@ -667,13 +660,6 @@
// Unregister all callbacks and unbind all services.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (serviceState.service != null) {
- if (serviceState.callback != null) {
- try {
- serviceState.service.unregisterCallback(serviceState.callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in unregisterCallback", e);
- }
- }
unbindService(serviceState);
}
}
@@ -3306,7 +3292,20 @@
return filteredDisplayName;
}
- private static final class UserState {
+ private class TvInputManagerCallbackList extends RemoteCallbackList<ITvInputManagerCallback> {
+ @Override
+ public void onCallbackDied(ITvInputManagerCallback callback) {
+ synchronized (mLock) {
+ for (int i = 0; i < mUserStates.size(); i++) {
+ int userId = mUserStates.keyAt(i);
+ UserState userState = getOrCreateUserStateLocked(userId);
+ userState.callbackPidUidMap.remove(callback);
+ }
+ }
+ }
+ }
+
+ private final class UserState {
// A mapping from the TV input id to its TvInputState.
private Map<String, TvInputState> inputMap = new HashMap<>();
@@ -3327,8 +3326,8 @@
private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>();
// A list of callbacks.
- private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks =
- new RemoteCallbackList<>();
+ private final TvInputManagerCallbackList mCallbacks =
+ new TvInputManagerCallbackList();
private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap =
new HashMap<>();
@@ -3558,12 +3557,19 @@
@GuardedBy("mLock")
private void unbindService(ServiceState serviceState) {
- if (!serviceState.bound) {
+ if (serviceState == null || !serviceState.bound) {
return;
}
if (DEBUG) {
Slog.d(TAG, "unbindService(service=" + serviceState.component + ")");
}
+ if (serviceState.callback != null) {
+ try {
+ serviceState.service.unregisterCallback(serviceState.callback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
+ }
mContext.unbindService(serviceState.connection);
serviceState.bound = false;
serviceState.service = null;
@@ -3781,9 +3787,9 @@
if (serviceState.hardwareInputMap.containsKey(inputInfo.getId())) {
return;
}
- Slog.d("ServiceCallback",
- "addHardwareInput: device id " + deviceId + ", "
- + inputInfo.toString());
+ Slog.d(TAG, "ServiceCallback: addHardwareInput, deviceId: " + deviceId +
+ ", inputInfo: " + inputInfo.toString() + " by " + mComponent +
+ ", userId: " + mUserId);
mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
addHardwareInputLocked(inputInfo, mComponent, mUserId);
}
@@ -3802,6 +3808,9 @@
if (serviceState.hardwareInputMap.containsKey(inputInfo.getId())) {
return;
}
+ Slog.d(TAG, "ServiceCallback: addHdmiInput, id: " + id +
+ ", inputInfo: "+ inputInfo.toString() + " by " + mComponent +
+ ", userId: " + mUserId);
mTvInputHardwareManager.addHdmiInput(id, inputInfo);
addHardwareInputLocked(inputInfo, mComponent, mUserId);
if (mOnScreenInputId != null && mOnScreenSessionState != null) {
@@ -3832,8 +3841,8 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- Slog.d("ServiceCallback",
- "removeHardwareInput " + inputId + " by " + mComponent);
+ Slog.d(TAG, "ServiceCallback: removeHardwareInput, inputId: " + inputId +
+ " by " + mComponent + ", userId: " + mUserId);
removeHardwareInputLocked(inputId, mUserId);
}
} finally {
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index edd2fa9..6a7fc6d 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -519,7 +519,7 @@
}
private void registerBroadcastReceivers() {
- PackageMonitor monitor = new PackageMonitor() {
+ PackageMonitor monitor = new PackageMonitor(/* supportsPackageRestartQuery */ true) {
private void buildTvInteractiveAppServiceList(String[] packages) {
int userId = getChangingUserId();
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
index 440d2514..eb5361c 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
@@ -26,6 +26,11 @@
* @hide
*/
public class CasResource {
+ /**
+ * Handle of the current resource. Should not be changed and should be aligned with the driver
+ * level implementation.
+ */
+ final int mHandle;
private final int mSystemId;
@@ -39,11 +44,16 @@
private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>();
CasResource(Builder builder) {
+ this.mHandle = builder.mHandle;
this.mSystemId = builder.mSystemId;
this.mMaxSessionNum = builder.mMaxSessionNum;
this.mAvailableSessionNum = builder.mMaxSessionNum;
}
+ public int getHandle() {
+ return mHandle;
+ }
+
public int getSystemId() {
return mSystemId;
}
@@ -136,10 +146,12 @@
*/
public static class Builder {
+ private final int mHandle;
private int mSystemId;
protected int mMaxSessionNum;
- Builder(int systemId) {
+ Builder(int handle, int systemId) {
+ this.mHandle = handle;
this.mSystemId = systemId;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
index 31149f3..5cef729 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
@@ -42,8 +42,8 @@
* Builder class for {@link CiCamResource}.
*/
public static class Builder extends CasResource.Builder {
- Builder(int systemId) {
- super(systemId);
+ Builder(int handle, int systemId) {
+ super(handle, systemId);
}
/**
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 0afb049..9229f7f 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -203,13 +203,7 @@
@Override
public void unregisterClientProfile(int clientId) throws RemoteException {
enforceTrmAccessPermission("unregisterClientProfile");
- synchronized (mLock) {
- if (!checkClientExists(clientId)) {
- Slog.e(TAG, "Unregistering non exists client:" + clientId);
- return;
- }
- unregisterClientProfileInternal(clientId);
- }
+ unregisterClientProfileInternal(clientId);
}
@Override
@@ -291,20 +285,7 @@
Slog.e(TAG, "frontendHandle can't be null");
return false;
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- Slog.e(TAG, "Request frontend from unregistered client: "
- + request.clientId);
- return false;
- }
- // If the request client is holding or sharing a frontend, throw an exception.
- if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
- Slog.e(TAG, "Release frontend before requesting another one. Client id: "
- + request.clientId);
- return false;
- }
- return requestFrontendInternal(request, frontendHandle);
- }
+ return requestFrontendInternal(request, frontendHandle);
}
@Override
@@ -376,13 +357,7 @@
if (demuxHandle == null) {
throw new RemoteException("demuxHandle can't be null");
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request demux from unregistered client:"
- + request.clientId);
- }
- return requestDemuxInternal(request, demuxHandle);
- }
+ return requestDemuxInternal(request, demuxHandle);
}
@Override
@@ -409,13 +384,7 @@
if (casSessionHandle == null) {
throw new RemoteException("casSessionHandle can't be null");
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request cas from unregistered client:"
- + request.clientId);
- }
- return requestCasSessionInternal(request, casSessionHandle);
- }
+ return requestCasSessionInternal(request, casSessionHandle);
}
@Override
@@ -425,13 +394,7 @@
if (ciCamHandle == null) {
throw new RemoteException("ciCamHandle can't be null");
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request ciCam from unregistered client:"
- + request.clientId);
- }
- return requestCiCamInternal(request, ciCamHandle);
- }
+ return requestCiCamInternal(request, ciCamHandle);
}
@Override
@@ -442,42 +405,14 @@
if (lnbHandle == null) {
throw new RemoteException("lnbHandle can't be null");
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request lnb from unregistered client:"
- + request.clientId);
- }
- return requestLnbInternal(request, lnbHandle);
- }
+ return requestLnbInternal(request, lnbHandle);
}
@Override
public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseFrontend");
enforceTrmAccessPermission("releaseFrontend");
- if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
- frontendHandle)) {
- throw new RemoteException("frontendHandle can't be invalid");
- }
- synchronized (mLock) {
- if (!checkClientExists(clientId)) {
- throw new RemoteException("Release frontend from unregistered client:"
- + clientId);
- }
- FrontendResource fe = getFrontendResource(frontendHandle);
- if (fe == null) {
- throw new RemoteException("Releasing frontend does not exist.");
- }
- int ownerClientId = fe.getOwnerClientId();
- ClientProfile ownerProfile = getClientProfile(ownerClientId);
- if (ownerClientId != clientId
- && (ownerProfile != null
- && !ownerProfile.getShareFeClientIds().contains(clientId))) {
- throw new RemoteException(
- "Client is not the current owner of the releasing fe.");
- }
- releaseFrontendInternal(fe, clientId);
- }
+ releaseFrontendInternal(frontendHandle, clientId);
}
@Override
@@ -746,17 +681,23 @@
@VisibleForTesting
protected void unregisterClientProfileInternal(int clientId) {
- if (DEBUG) {
- Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
- }
- removeClientProfile(clientId);
- // Remove the Media Resource Manager callingPid to tvAppId mapping
- if (mMediaResourceManager != null) {
- try {
- mMediaResourceManager.overridePid(Binder.getCallingPid(), -1);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister,"
- + " remote exception: " + e);
+ synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ Slog.e(TAG, "Unregistering non exists client:" + clientId);
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+ }
+ removeClientProfile(clientId);
+ // Remove the Media Resource Manager callingPid to tvAppId mapping
+ if (mMediaResourceManager != null) {
+ try {
+ mMediaResourceManager.overridePid(Binder.getCallingPid(), -1);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister,"
+ + " remote exception: " + e);
+ }
}
}
}
@@ -992,10 +933,14 @@
return;
}
// Add the new Cas Resource.
- cas = new CasResource.Builder(casSystemId)
+ int casSessionHandle = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSystemId);
+ cas = new CasResource.Builder(casSessionHandle, casSystemId)
.maxSessionNum(maxSessionNum)
.build();
- ciCam = new CiCamResource.Builder(casSystemId)
+ int ciCamHandle = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, casSystemId);
+ ciCam = new CiCamResource.Builder(ciCamHandle, casSystemId)
.maxSessionNum(maxSessionNum)
.build();
addCasResource(cas);
@@ -1007,86 +952,120 @@
if (DEBUG) {
Slog.d(TAG, "requestFrontend(request=" + request + ")");
}
-
- frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
- // TODO: check if this is really needed
- if (requestClient == null) {
+ int[] reclaimOwnerId = new int[1];
+ if (!claimFrontend(request, frontendHandle, reclaimOwnerId)) {
return false;
}
- clientPriorityUpdateOnRequest(requestClient);
- int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- // If the desired frontend id was specified, we only need to check the frontend.
- boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest.DEFAULT_DESIRED_ID;
- for (FrontendResource fr : getFrontendResources().values()) {
- int frontendId = getResourceIdFromHandle(fr.getHandle());
- if (fr.getType() == request.frontendType
- && (!hasDesiredFrontend || frontendId == request.desiredId)) {
- if (!fr.isInUse()) {
- // Unused resource cannot be acquired if the max is already reached, but
- // TRM still has to look for the reclaim candidate
- if (isFrontendMaxNumUseReached(request.frontendType)) {
- continue;
- }
- // Grant unused frontend with no exclusive group members first.
- if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
- grantingFrontendHandle = fr.getHandle();
- break;
- } else if (grantingFrontendHandle
- == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- // Grant the unused frontend with lower id first if all the unused
- // frontends have exclusive group members.
- grantingFrontendHandle = fr.getHandle();
- }
- } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- // Record the frontend id with the lowest client priority among all the
- // in use frontends when no available frontend has been found.
- int priority = getFrontendHighestClientPriority(fr.getOwnerClientId());
- if (currentLowestPriority > priority) {
- // we need to check the max used num if the target frontend type is not
- // currently in primary use (and simply blocked due to exclusive group)
- ClientProfile targetOwnerProfile = getClientProfile(fr.getOwnerClientId());
- int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
- FrontendResource primaryFe = getFrontendResource(primaryFeId);
- if (fr.getType() != primaryFe.getType()
- && isFrontendMaxNumUseReached(fr.getType())) {
+ if (frontendHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
+ }
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0], TunerResourceManager
+ .TUNER_RESOURCE_TYPE_FRONTEND)) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (getFrontendResource(frontendHandle[0]).isInUse()) {
+ Slog.e(TAG, "Reclaimed frontend still in use");
+ return false;
+ }
+ updateFrontendClientMappingOnNewGrant(frontendHandle[0], request.clientId);
+ }
+ }
+ return true;
+ }
+
+ protected boolean claimFrontend(
+ TunerFrontendRequest request,
+ int[] frontendHandle,
+ int[] reclaimOwnerId
+ ) {
+ frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ Slog.e(TAG, "Request frontend from unregistered client: "
+ + request.clientId);
+ return false;
+ }
+ // If the request client is holding or sharing a frontend, throw an exception.
+ if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
+ Slog.e(TAG, "Release frontend before requesting another one. Client id: "
+ + request.clientId);
+ return false;
+ }
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ FrontendResource grantingFrontend = null;
+ FrontendResource inUseLowestPriorityFrontend = null;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ // If the desired frontend id was specified, we only need to check the frontend.
+ boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest
+ .DEFAULT_DESIRED_ID;
+ for (FrontendResource fr : getFrontendResources().values()) {
+ int frontendId = getResourceIdFromHandle(fr.getHandle());
+ if (fr.getType() == request.frontendType
+ && (!hasDesiredFrontend || frontendId == request.desiredId)) {
+ if (!fr.isInUse()) {
+ // Unused resource cannot be acquired if the max is already reached, but
+ // TRM still has to look for the reclaim candidate
+ if (isFrontendMaxNumUseReached(request.frontendType)) {
continue;
}
- // update the target frontend
- inUseLowestPriorityFrHandle = fr.getHandle();
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(fr.getOwnerClientId())).getProcessId());
+ // Grant unused frontend with no exclusive group members first.
+ if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
+ grantingFrontend = fr;
+ break;
+ } else if (grantingFrontend == null) {
+ // Grant the unused frontend with lower id first if all the unused
+ // frontends have exclusive group members.
+ grantingFrontend = fr;
+ }
+ } else if (grantingFrontend == null) {
+ // Record the frontend id with the lowest client priority among all the
+ // in use frontends when no available frontend has been found.
+ int priority = getFrontendHighestClientPriority(fr.getOwnerClientId());
+ if (currentLowestPriority > priority) {
+ // we need to check the max used num if the target frontend type is not
+ // currently in primary use (and simply blocked due to exclusive group)
+ ClientProfile targetOwnerProfile =
+ getClientProfile(fr.getOwnerClientId());
+ int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
+ FrontendResource primaryFe = getFrontendResource(primaryFeId);
+ if (fr.getType() != primaryFe.getType()
+ && isFrontendMaxNumUseReached(fr.getType())) {
+ continue;
+ }
+ // update the target frontend
+ inUseLowestPriorityFrontend = fr;
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(fr.getOwnerClientId())).getProcessId());
+ }
}
}
}
- }
- // Grant frontend when there is unused resource.
- if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- frontendHandle[0] = grantingFrontendHandle;
- updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.clientId);
- return true;
- }
-
- // When all the resources are occupied, grant the lowest priority resource if the
- // request client has higher priority.
- if (inUseLowestPriorityFrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE
- && ((requestClient.getPriority() > currentLowestPriority) || (
- (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
- if (!reclaimResource(
- getFrontendResource(inUseLowestPriorityFrHandle).getOwnerClientId(),
- TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
- return false;
+ // Grant frontend when there is unused resource.
+ if (grantingFrontend != null) {
+ updateFrontendClientMappingOnNewGrant(grantingFrontend.getHandle(),
+ request.clientId);
+ frontendHandle[0] = grantingFrontend.getHandle();
+ return true;
}
- frontendHandle[0] = inUseLowestPriorityFrHandle;
- updateFrontendClientMappingOnNewGrant(
- inUseLowestPriorityFrHandle, request.clientId);
- return true;
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityFrontend != null
+ && ((requestClient.getPriority() > currentLowestPriority)
+ || ((requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ frontendHandle[0] = inUseLowestPriorityFrontend.getHandle();
+ reclaimOwnerId[0] = inUseLowestPriorityFrontend.getOwnerClientId();
+ return true;
+ }
}
return false;
@@ -1192,165 +1171,257 @@
}
@VisibleForTesting
- protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) {
+ protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle)
+ throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestLnb(request=" + request + ")");
}
-
- lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
- clientPriorityUpdateOnRequest(requestClient);
- int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- for (LnbResource lnb : getLnbResources().values()) {
- if (!lnb.isInUse()) {
- // Grant the unused lnb with lower handle first
- grantingLnbHandle = lnb.getHandle();
- break;
- } else {
- // Record the lnb id with the lowest client priority among all the
- // in use lnb when no available lnb has been found.
- int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
- if (currentLowestPriority > priority) {
- inUseLowestPriorityLnbHandle = lnb.getHandle();
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(lnb.getOwnerClientId())).getProcessId());
- }
- }
+ int[] reclaimOwnerId = new int[1];
+ if (!claimLnb(request, lnbHandle, reclaimOwnerId)) {
+ return false;
}
-
- // Grant Lnb when there is unused resource.
- if (grantingLnbHandle > -1) {
- lnbHandle[0] = grantingLnbHandle;
- updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId);
- return true;
+ if (lnbHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
}
-
- // When all the resources are occupied, grant the lowest priority resource if the
- // request client has higher priority.
- if (inUseLowestPriorityLnbHandle > TunerResourceManager.INVALID_RESOURCE_HANDLE
- && ((requestClient.getPriority() > currentLowestPriority) || (
- (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
- if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbHandle).getOwnerClientId(),
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0],
TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) {
return false;
}
- lnbHandle[0] = inUseLowestPriorityLnbHandle;
- updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId);
- return true;
+ synchronized (mLock) {
+ if (getLnbResource(lnbHandle[0]).isInUse()) {
+ Slog.e(TAG, "Reclaimed lnb still in use");
+ return false;
+ }
+ updateLnbClientMappingOnNewGrant(lnbHandle[0], request.clientId);
+ }
+ }
+ return true;
+ }
+
+ protected boolean claimLnb(TunerLnbRequest request, int[] lnbHandle, int[] reclaimOwnerId)
+ throws RemoteException {
+ lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request lnb from unregistered client:"
+ + request.clientId);
+ }
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ LnbResource grantingLnb = null;
+ LnbResource inUseLowestPriorityLnb = null;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ for (LnbResource lnb : getLnbResources().values()) {
+ if (!lnb.isInUse()) {
+ // Grant the unused lnb with lower handle first
+ grantingLnb = lnb;
+ break;
+ } else {
+ // Record the lnb id with the lowest client priority among all the
+ // in use lnb when no available lnb has been found.
+ int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
+ if (currentLowestPriority > priority) {
+ inUseLowestPriorityLnb = lnb;
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(lnb.getOwnerClientId())).getProcessId());
+ }
+ }
+ }
+
+ // Grant Lnb when there is unused resource.
+ if (grantingLnb != null) {
+ updateLnbClientMappingOnNewGrant(grantingLnb.getHandle(), request.clientId);
+ lnbHandle[0] = grantingLnb.getHandle();
+ return true;
+ }
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityLnb != null
+ && ((requestClient.getPriority() > currentLowestPriority) || (
+ (requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ lnbHandle[0] = inUseLowestPriorityLnb.getHandle();
+ reclaimOwnerId[0] = inUseLowestPriorityLnb.getOwnerClientId();
+ return true;
+ }
}
return false;
}
@VisibleForTesting
- protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) {
+ protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle)
+ throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestCasSession(request=" + request + ")");
}
- CasResource cas = getCasResource(request.casSystemId);
- // Unregistered Cas System is treated as having unlimited sessions.
- if (cas == null) {
- cas = new CasResource.Builder(request.casSystemId)
- .maxSessionNum(Integer.MAX_VALUE)
- .build();
- addCasResource(cas);
+ int[] reclaimOwnerId = new int[1];
+ if (!claimCasSession(request, casSessionHandle, reclaimOwnerId)) {
+ return false;
}
- casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
- clientPriorityUpdateOnRequest(requestClient);
- int lowestPriorityOwnerId = -1;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- if (!cas.isFullyUsed()) {
- casSessionHandle[0] = generateResourceHandle(
- TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
- updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
- return true;
+ if (casSessionHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
}
- for (int ownerId : cas.getOwnerClientIds()) {
- // Record the client id with lowest priority that is using the current Cas system.
- int priority = updateAndGetOwnerClientPriority(ownerId);
- if (currentLowestPriority > priority) {
- lowestPriorityOwnerId = ownerId;
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(ownerId)).getProcessId());
- }
- }
-
- // When all the Cas sessions are occupied, reclaim the lowest priority client if the
- // request client has higher priority.
- if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority)
- || ((requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
- if (!reclaimResource(lowestPriorityOwnerId,
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0],
TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) {
return false;
}
- casSessionHandle[0] = generateResourceHandle(
- TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
- updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
- return true;
+ synchronized (mLock) {
+ if (getCasResource(request.casSystemId).isFullyUsed()) {
+ Slog.e(TAG, "Reclaimed cas still fully used");
+ return false;
+ }
+ updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
+ }
}
+ return true;
+ }
+
+ protected boolean claimCasSession(CasSessionRequest request, int[] casSessionHandle,
+ int[] reclaimOwnerId) throws RemoteException {
+ casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request cas from unregistered client:"
+ + request.clientId);
+ }
+ CasResource cas = getCasResource(request.casSystemId);
+ // Unregistered Cas System is treated as having unlimited sessions.
+ if (cas == null) {
+ int resourceHandle = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, request.clientId);
+ cas = new CasResource.Builder(resourceHandle, request.casSystemId)
+ .maxSessionNum(Integer.MAX_VALUE)
+ .build();
+ addCasResource(cas);
+ }
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ int lowestPriorityOwnerId = INVALID_CLIENT_ID;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ if (!cas.isFullyUsed()) {
+ updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
+ casSessionHandle[0] = cas.getHandle();
+ return true;
+ }
+ for (int ownerId : cas.getOwnerClientIds()) {
+ // Record the client id with lowest priority that is using the current Cas system.
+ int priority = updateAndGetOwnerClientPriority(ownerId);
+ if (currentLowestPriority > priority) {
+ lowestPriorityOwnerId = ownerId;
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(ownerId)).getProcessId());
+ }
+ }
+
+ // When all the Cas sessions are occupied, reclaim the lowest priority client if the
+ // request client has higher priority.
+ if (lowestPriorityOwnerId != INVALID_CLIENT_ID
+ && ((requestClient.getPriority() > currentLowestPriority)
+ || ((requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ casSessionHandle[0] = cas.getHandle();
+ reclaimOwnerId[0] = lowestPriorityOwnerId;
+ return true;
+ }
+ }
+
return false;
}
@VisibleForTesting
- protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) {
+ protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle)
+ throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")");
}
- CiCamResource ciCam = getCiCamResource(request.ciCamId);
- // Unregistered Cas System is treated as having unlimited sessions.
- if (ciCam == null) {
- ciCam = new CiCamResource.Builder(request.ciCamId)
- .maxSessionNum(Integer.MAX_VALUE)
- .build();
- addCiCamResource(ciCam);
+ int[] reclaimOwnerId = new int[1];
+ if (!claimCiCam(request, ciCamHandle, reclaimOwnerId)) {
+ return false;
}
- ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
- clientPriorityUpdateOnRequest(requestClient);
- int lowestPriorityOwnerId = -1;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- if (!ciCam.isFullyUsed()) {
- ciCamHandle[0] = generateResourceHandle(
- TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
- updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
- return true;
+ if (ciCamHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
}
- for (int ownerId : ciCam.getOwnerClientIds()) {
- // Record the client id with lowest priority that is using the current Cas system.
- int priority = updateAndGetOwnerClientPriority(ownerId);
- if (currentLowestPriority > priority) {
- lowestPriorityOwnerId = ownerId;
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(ownerId)).getProcessId());
- }
- }
-
- // When all the CiCam sessions are occupied, reclaim the lowest priority client if the
- // request client has higher priority.
- if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority)
- || ((requestClient.getPriority() == currentLowestPriority)
- && isRequestFromSameProcess))) {
- if (!reclaimResource(lowestPriorityOwnerId,
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0],
TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM)) {
return false;
}
- ciCamHandle[0] = generateResourceHandle(
- TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
- updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
- return true;
+ synchronized (mLock) {
+ if (getCiCamResource(request.ciCamId).isFullyUsed()) {
+ Slog.e(TAG, "Reclaimed ciCam still fully used");
+ return false;
+ }
+ updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+ }
}
+ return true;
+ }
+
+ protected boolean claimCiCam(TunerCiCamRequest request, int[] ciCamHandle,
+ int[] reclaimOwnerId) throws RemoteException {
+ ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request ciCam from unregistered client:"
+ + request.clientId);
+ }
+ CiCamResource ciCam = getCiCamResource(request.ciCamId);
+ // Unregistered CiCam is treated as having unlimited sessions.
+ if (ciCam == null) {
+ int resourceHandle = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, request.ciCamId);
+ ciCam = new CiCamResource.Builder(resourceHandle, request.ciCamId)
+ .maxSessionNum(Integer.MAX_VALUE)
+ .build();
+ addCiCamResource(ciCam);
+ }
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ int lowestPriorityOwnerId = INVALID_CLIENT_ID;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ if (!ciCam.isFullyUsed()) {
+ updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+ ciCamHandle[0] = ciCam.getHandle();
+ return true;
+ }
+ for (int ownerId : ciCam.getOwnerClientIds()) {
+ // Record the client id with lowest priority that is using the current CiCam.
+ int priority = updateAndGetOwnerClientPriority(ownerId);
+ if (currentLowestPriority > priority) {
+ lowestPriorityOwnerId = ownerId;
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(ownerId)).getProcessId());
+ }
+ }
+
+ // When all the CiCam sessions are occupied, reclaim the lowest priority client if the
+ // request client has higher priority.
+ if (lowestPriorityOwnerId != INVALID_CLIENT_ID
+ && ((requestClient.getPriority() > currentLowestPriority)
+ || ((requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ ciCamHandle[0] = ciCam.getHandle();
+ reclaimOwnerId[0] = lowestPriorityOwnerId;
+ return true;
+ }
+ }
+
return false;
}
@@ -1383,20 +1454,49 @@
}
@VisibleForTesting
- protected void releaseFrontendInternal(FrontendResource fe, int clientId) {
+ protected void releaseFrontendInternal(int frontendHandle, int clientId)
+ throws RemoteException {
if (DEBUG) {
- Slog.d(TAG, "releaseFrontend(id=" + fe.getHandle() + ", clientId=" + clientId + " )");
+ Slog.d(TAG, "releaseFrontend(id=" + frontendHandle + ", clientId=" + clientId + " )");
}
- if (clientId == fe.getOwnerClientId()) {
- ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
- if (ownerClient != null) {
- for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
- reclaimResource(shareOwnerId,
- TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
+ if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
+ frontendHandle)) {
+ throw new RemoteException("frontendHandle can't be invalid");
+ }
+ Set<Integer> reclaimedResourceOwnerIds = unclaimFrontend(frontendHandle, clientId);
+ if (reclaimedResourceOwnerIds != null) {
+ for (int shareOwnerId : reclaimedResourceOwnerIds) {
+ reclaimResource(shareOwnerId,
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
+ }
+ }
+ synchronized (mLock) {
+ clearFrontendAndClientMapping(getClientProfile(clientId));
+ }
+ }
+
+ private Set<Integer> unclaimFrontend(int frontendHandle, int clientId) throws RemoteException {
+ Set<Integer> reclaimedResourceOwnerIds = null;
+ synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ throw new RemoteException("Release frontend from unregistered client:"
+ + clientId);
+ }
+ FrontendResource fe = getFrontendResource(frontendHandle);
+ if (fe == null) {
+ throw new RemoteException("Releasing frontend does not exist.");
+ }
+ int ownerClientId = fe.getOwnerClientId();
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ if (ownerClientId == clientId) {
+ reclaimedResourceOwnerIds = ownerProfile.getShareFeClientIds();
+ } else {
+ if (!ownerProfile.getShareFeClientIds().contains(clientId)) {
+ throw new RemoteException("Client is not a sharee of the releasing fe.");
}
}
}
- clearFrontendAndClientMapping(getClientProfile(clientId));
+ return reclaimedResourceOwnerIds;
}
@VisibleForTesting
@@ -1432,103 +1532,129 @@
}
@VisibleForTesting
- protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
+ public boolean requestDemuxInternal(@NonNull TunerDemuxRequest request,
+ @NonNull int[] demuxHandle) throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestDemux(request=" + request + ")");
}
-
- // For Tuner 2.0 and below or any HW constraint devices that are unable to support
- // ITuner.openDemuxById(), demux resources are not really managed under TRM and
- // mDemuxResources.size() will be zero
- if (mDemuxResources.size() == 0) {
- // There are enough Demux resources, so we don't manage Demux in R.
- demuxHandle[0] =
- generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
- return true;
- }
-
- demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
-
- if (requestClient == null) {
+ int[] reclaimOwnerId = new int[1];
+ if (!claimDemux(request, demuxHandle, reclaimOwnerId)) {
return false;
}
+ if (demuxHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
+ }
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0],
+ TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (getDemuxResource(demuxHandle[0]).isInUse()) {
+ Slog.e(TAG, "Reclaimed demux still in use");
+ return false;
+ }
+ updateDemuxClientMappingOnNewGrant(demuxHandle[0], request.clientId);
+ }
+ }
+ return true;
+ }
- clientPriorityUpdateOnRequest(requestClient);
- int grantingDemuxHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityDrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- // If the desired demux id was specified, we only need to check the demux.
- boolean hasDesiredDemuxCap = request.desiredFilterTypes
- != DemuxFilterMainType.UNDEFINED;
- int smallestNumOfSupportedCaps = Integer.SIZE + 1;
- int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
- for (DemuxResource dr : getDemuxResources().values()) {
- if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
- if (!dr.isInUse()) {
- int numOfSupportedCaps = dr.getNumOfCaps();
+ protected boolean claimDemux(TunerDemuxRequest request, int[] demuxHandle, int[] reclaimOwnerId)
+ throws RemoteException {
+ demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request demux from unregistered client:"
+ + request.clientId);
+ }
- // look for the demux with minimum caps supporting the desired caps
- if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
- smallestNumOfSupportedCaps = numOfSupportedCaps;
- grantingDemuxHandle = dr.getHandle();
- }
- } else if (grantingDemuxHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- // Record the demux id with the lowest client priority among all the
- // in use demuxes when no availabledemux has been found.
- int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId());
- if (currentLowestPriority >= priority) {
+ // For Tuner 2.0 and below or any HW constraint devices that are unable to support
+ // ITuner.openDemuxById(), demux resources are not really managed under TRM and
+ // mDemuxResources.size() will be zero
+ if (mDemuxResources.size() == 0) {
+ // There are enough Demux resources, so we don't manage Demux in R.
+ demuxHandle[0] =
+ generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
+ return true;
+ }
+
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ if (requestClient == null) {
+ return false;
+ }
+ clientPriorityUpdateOnRequest(requestClient);
+ DemuxResource grantingDemux = null;
+ DemuxResource inUseLowestPriorityDemux = null;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ // If the desired demux id was specified, we only need to check the demux.
+ boolean hasDesiredDemuxCap = request.desiredFilterTypes
+ != DemuxFilterMainType.UNDEFINED;
+ int smallestNumOfSupportedCaps = Integer.SIZE + 1;
+ int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
+ for (DemuxResource dr : getDemuxResources().values()) {
+ if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
+ if (!dr.isInUse()) {
int numOfSupportedCaps = dr.getNumOfCaps();
- boolean shouldUpdate = false;
- // update lowest priority
- if (currentLowestPriority > priority) {
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(dr.getOwnerClientId())).getProcessId());
- // reset the smallest caps when lower priority resource is found
- smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
-
- shouldUpdate = true;
- } else {
- // This is the case when the priority is the same as previously found
- // one. Update smallest caps when priority.
- if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
- smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
- shouldUpdate = true;
- }
+ // look for the demux with minimum caps supporting the desired caps
+ if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
+ smallestNumOfSupportedCaps = numOfSupportedCaps;
+ grantingDemux = dr;
}
- if (shouldUpdate) {
- inUseLowestPriorityDrHandle = dr.getHandle();
+ } else if (grantingDemux == null) {
+ // Record the demux id with the lowest client priority among all the
+ // in use demuxes when no availabledemux has been found.
+ int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId());
+ if (currentLowestPriority >= priority) {
+ int numOfSupportedCaps = dr.getNumOfCaps();
+ boolean shouldUpdate = false;
+ // update lowest priority
+ if (currentLowestPriority > priority) {
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(dr.getOwnerClientId())).getProcessId());
+
+ // reset the smallest caps when lower priority resource is found
+ smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+
+ shouldUpdate = true;
+ } else {
+ // This is the case when the priority is the same as previously
+ // found one. Update smallest caps when priority.
+ if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
+ smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+ shouldUpdate = true;
+ }
+ }
+ if (shouldUpdate) {
+ inUseLowestPriorityDemux = dr;
+ }
}
}
}
}
- }
- // Grant demux when there is unused resource.
- if (grantingDemuxHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- demuxHandle[0] = grantingDemuxHandle;
- updateDemuxClientMappingOnNewGrant(grantingDemuxHandle, request.clientId);
- return true;
- }
-
- // When all the resources are occupied, grant the lowest priority resource if the
- // request client has higher priority.
- if (inUseLowestPriorityDrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE
- && ((requestClient.getPriority() > currentLowestPriority) || (
- (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
- if (!reclaimResource(
- getDemuxResource(inUseLowestPriorityDrHandle).getOwnerClientId(),
- TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
- return false;
+ // Grant demux when there is unused resource.
+ if (grantingDemux != null) {
+ updateDemuxClientMappingOnNewGrant(grantingDemux.getHandle(), request.clientId);
+ demuxHandle[0] = grantingDemux.getHandle();
+ return true;
}
- demuxHandle[0] = inUseLowestPriorityDrHandle;
- updateDemuxClientMappingOnNewGrant(
- inUseLowestPriorityDrHandle, request.clientId);
- return true;
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityDemux != null
+ && ((requestClient.getPriority() > currentLowestPriority) || (
+ (requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ demuxHandle[0] = inUseLowestPriorityDemux.getHandle();
+ reclaimOwnerId[0] = inUseLowestPriorityDemux.getOwnerClientId();
+ return true;
+ }
}
return false;
@@ -1792,7 +1918,9 @@
return;
}
- mListeners.put(clientId, record);
+ synchronized (mLock) {
+ mListeners.put(clientId, record);
+ }
}
@VisibleForTesting
@@ -1808,33 +1936,44 @@
// Reclaim all the resources of the share owners of the frontend that is used by the current
// resource reclaimed client.
- ClientProfile profile = getClientProfile(reclaimingClientId);
- // TODO: check if this check is really needed.
- if (profile == null) {
- return true;
- }
- Set<Integer> shareFeClientIds = profile.getShareFeClientIds();
- for (int clientId : shareFeClientIds) {
- try {
- mListeners.get(clientId).getListener().onReclaimResources();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e);
- return false;
+ Set<Integer> shareFeClientIds;
+ synchronized (mLock) {
+ ClientProfile profile = getClientProfile(reclaimingClientId);
+ if (profile == null) {
+ return true;
}
- clearAllResourcesAndClientMapping(getClientProfile(clientId));
+ shareFeClientIds = profile.getShareFeClientIds();
+ }
+ ResourcesReclaimListenerRecord listenerRecord = null;
+ for (int clientId : shareFeClientIds) {
+ synchronized (mLock) {
+ listenerRecord = mListeners.get(clientId);
+ }
+ if (listenerRecord != null) {
+ try {
+ listenerRecord.getListener().onReclaimResources();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e);
+ }
+ }
}
if (DEBUG) {
Slog.d(TAG, "Reclaiming resources because higher priority client request resource type "
+ resourceType + ", clientId:" + reclaimingClientId);
}
- try {
- mListeners.get(reclaimingClientId).getListener().onReclaimResources();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
- return false;
+
+ synchronized (mLock) {
+ listenerRecord = mListeners.get(reclaimingClientId);
}
- clearAllResourcesAndClientMapping(profile);
+ if (listenerRecord != null) {
+ try {
+ listenerRecord.getListener().onReclaimResources();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
+ }
+ }
+
return true;
}
@@ -2258,6 +2397,7 @@
addResourcesReclaimListener(clientId, listener);
}
+ @SuppressWarnings("GuardedBy") // Lock is held on mListeners
private void removeClientProfile(int clientId) {
for (int shareOwnerId : getClientProfile(clientId).getShareFeClientIds()) {
clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
@@ -2265,12 +2405,9 @@
clearAllResourcesAndClientMapping(getClientProfile(clientId));
mClientProfiles.remove(clientId);
- // it may be called by unregisterClientProfileInternal under test
- synchronized (mLock) {
- ResourcesReclaimListenerRecord record = mListeners.remove(clientId);
- if (record != null) {
- record.getListener().asBinder().unlinkToDeath(record, 0);
- }
+ ResourcesReclaimListenerRecord record = mListeners.remove(clientId);
+ if (record != null) {
+ record.getListener().asBinder().unlinkToDeath(record, 0);
}
}
@@ -2304,7 +2441,8 @@
profile.releaseFrontend();
}
- private void clearAllResourcesAndClientMapping(ClientProfile profile) {
+ @VisibleForTesting
+ protected void clearAllResourcesAndClientMapping(ClientProfile profile) {
// TODO: check if this check is really needed. Maybe needed for reclaimResource path.
if (profile == null) {
return;
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index ecd140e..96a25da 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -44,7 +44,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
@@ -324,11 +323,8 @@
if (SubscriptionManager.isValidSubscriptionId(subId)) {
// Get only configs as needed to save memory.
final PersistableBundle carrierConfig =
- Flags.fixCrashOnGettingConfigWhenPhoneIsGone()
- ? CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
- VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS)
- : mCarrierConfigManager.getConfigForSubId(subId,
- VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+ CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
+ VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
mReadySubIdsBySlotId.put(slotId, subId);
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 47425322..5f704a0 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -276,7 +276,7 @@
// enabled on the last one as a sample
mInboundTransform = inboundTransform;
- if (!Flags.allowDisableIpsecLossDetector() || canStart()) {
+ if (canStart()) {
start();
}
}
@@ -292,7 +292,7 @@
mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
}
- if (Flags.allowDisableIpsecLossDetector() && canStart() != isStarted()) {
+ if (canStart() != isStarted()) {
if (canStart()) {
start();
} else {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index f53dda6..78359bd 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2224,15 +2224,21 @@
public ParcelFileDescriptor getWallpaperWithFeature(String callingPkg, String callingFeatureId,
IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId,
boolean getCropped) {
- final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL)
- || hasPermission(MANAGE_EXTERNAL_STORAGE);
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL);
if (!hasPrivilege) {
- mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
- Binder.getCallingPid(), Binder.getCallingUid(), callingPkg, callingFeatureId);
+ boolean hasManageExternalStorage = hasPermission(MANAGE_EXTERNAL_STORAGE)
+ || hasAppOpPermission(MANAGE_EXTERNAL_STORAGE, callingUid, callingPkg,
+ callingFeatureId, "getWallpaperWithFeature from package: " + callingPkg);
+ if (!hasManageExternalStorage) {
+ mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
+ callingPid, callingUid, callingPkg, callingFeatureId);
+ }
}
- wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
+ wallpaperUserId = ActivityManager.handleIncomingUser(callingPid, callingUid,
+ wallpaperUserId, false, true, "getWallpaper", null);
if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
@@ -2348,6 +2354,22 @@
return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
+ private boolean hasAppOpPermission(String permission, int callingUid, String callingPackage,
+ String attributionTag, String message) {
+ final String op = AppOpsManager.permissionToOp(permission);
+ final int opMode = mAppOpsManager.noteOpNoThrow(op, callingUid, callingPackage,
+ attributionTag, message);
+ switch (opMode) {
+ case AppOpsManager.MODE_ALLOWED:
+ case AppOpsManager.MODE_FOREGROUND:
+ return true;
+ case AppOpsManager.MODE_DEFAULT:
+ return hasPermission(permission);
+ default:
+ return false;
+ }
+ }
+
@Override
public WallpaperInfo getWallpaperInfo(int userId) {
return getWallpaperInfoWithFlags(FLAG_SYSTEM, userId);
@@ -3169,7 +3191,8 @@
final WallpaperDestinationChangeHandler
liveSync = new WallpaperDestinationChangeHandler(
newWallpaper);
- boolean same = changingToSame(name, newWallpaper);
+ boolean same = changingToSame(name, newWallpaper.connection,
+ newWallpaper.wallpaperComponent);
/*
* If we have a shared system+lock wallpaper, and we reapply the same wallpaper
@@ -3257,14 +3280,15 @@
return name == null || name.equals(mDefaultWallpaperComponent);
}
- private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) {
- if (wallpaper.connection != null) {
- final ComponentName wallpaperName = wallpaper.wallpaperComponent;
- if (isDefaultComponent(componentName) && isDefaultComponent(wallpaperName)) {
+ private boolean changingToSame(ComponentName newComponentName,
+ WallpaperConnection currentConnection, ComponentName currentComponentName) {
+ if (currentConnection != null) {
+ if (isDefaultComponent(newComponentName) && isDefaultComponent(currentComponentName)) {
if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
// Still using default wallpaper.
return true;
- } else if (wallpaperName != null && wallpaperName.equals(componentName)) {
+ } else if (currentComponentName != null && currentComponentName.equals(
+ newComponentName)) {
// Changing to same wallpaper.
if (DEBUG) Slog.v(TAG, "same wallpaper");
return true;
@@ -3279,7 +3303,8 @@
Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
}
// Has the component changed?
- if (!force && changingToSame(componentName, wallpaper)) {
+ if (!force && changingToSame(componentName, wallpaper.connection,
+ wallpaper.wallpaperComponent)) {
try {
if (DEBUG_LIVE) {
Slog.v(TAG, "Changing to the same component, ignoring");
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index c4d601d..6740153 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -19,14 +19,12 @@
import static android.webkit.Flags.updateServiceV2;
import android.app.ActivityManager;
-import android.app.AppGlobals;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.os.RemoteException;
@@ -79,7 +77,7 @@
XmlResourceParser parser = null;
List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
try {
- parser = AppGlobals.getInitialApplication().getResources().getXml(
+ parser = mContext.getResources().getXml(
com.android.internal.R.xml.config_webview_packages);
XmlUtils.beginDocument(parser, TAG_START);
while(true) {
@@ -148,7 +146,7 @@
}
public long getFactoryPackageVersion(String packageName) throws NameNotFoundException {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ PackageManager pm = mContext.getPackageManager();
return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY)
.getLongVersionCode();
}
@@ -203,47 +201,48 @@
@Override
public void enablePackageForAllUsers(String packageName, boolean enable) {
UserManager userManager = mContext.getSystemService(UserManager.class);
- for(UserInfo userInfo : userManager.getUsers()) {
- enablePackageForUser(packageName, enable, userInfo.id);
+ for (UserHandle user : userManager.getUserHandles(false)) {
+ enablePackageForUser(packageName, enable, user);
}
}
- private void enablePackageForUser(String packageName, boolean enable, int userId) {
+ private void enablePackageForUser(String packageName, boolean enable, UserHandle user) {
+ Context contextAsUser = mContext.createContextAsUser(user, 0);
+ PackageManager pm = contextAsUser.getPackageManager();
try {
- AppGlobals.getPackageManager().setApplicationEnabledSetting(
+ pm.setApplicationEnabledSetting(
packageName,
enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
- userId, null);
- } catch (RemoteException | IllegalArgumentException e) {
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0);
+ } catch (IllegalArgumentException e) {
Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName
- + " for user " + userId + ": " + e);
+ + " for user " + user + ": " + e);
}
}
@Override
public void installExistingPackageForAllUsers(String packageName) {
UserManager userManager = mContext.getSystemService(UserManager.class);
- for (UserInfo userInfo : userManager.getUsers()) {
- installPackageForUser(packageName, userInfo.id);
+ for (UserHandle user : userManager.getUserHandles(false)) {
+ installPackageForUser(packageName, user);
}
}
- private void installPackageForUser(String packageName, int userId) {
- final Context contextAsUser = mContext.createContextAsUser(UserHandle.of(userId), 0);
- final PackageInstaller installer = contextAsUser.getPackageManager().getPackageInstaller();
+ private void installPackageForUser(String packageName, UserHandle user) {
+ Context contextAsUser = mContext.createContextAsUser(user, 0);
+ PackageInstaller installer = contextAsUser.getPackageManager().getPackageInstaller();
installer.installExistingPackage(packageName, PackageManager.INSTALL_REASON_UNKNOWN, null);
}
@Override
public boolean systemIsDebuggable() {
- return Build.IS_DEBUGGABLE;
+ return Build.isDebuggable();
}
@Override
public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
throws NameNotFoundException {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ PackageManager pm = mContext.getPackageManager();
return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
}
@@ -327,5 +326,5 @@
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
| PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER;
+ | PackageManager.MATCH_ANY_USER;
}
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/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 2ce1aa42..fb5c1154 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -838,12 +838,13 @@
}
if (android.app.Flags.appStartInfoTimestamps()) {
+ final int pid = r.getPid();
// Log here to match StatsD for time to first frame.
mLoggerHandler.post(
() -> mSupervisor.mService.mWindowManager.mAmInternal.addStartInfoTimestamp(
ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME,
- timestampNs, r.getUid(), r.getPid(),
- info.mLastLaunchedActivity.mUserId));
+ timestampNs, infoSnapshot.applicationInfo.uid, pid,
+ infoSnapshot.userId));
}
return infoSnapshot;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 235a211..1dfa063 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;
@@ -813,6 +812,8 @@
/** The last set {@link DropInputMode} for this activity surface. */
@DropInputMode
private int mLastDropInputMode = DropInputMode.NONE;
+ /** Whether the input to this activity will be dropped during the current playing animation. */
+ private boolean mIsInputDroppedForAnimation;
/**
* Whether the application has desk mode resources. Calculated and cached when
@@ -1647,6 +1648,15 @@
}
}
+ /** Sets if all input will be dropped as a protection during the client-driven animation. */
+ void setDropInputForAnimation(boolean isInputDroppedForAnimation) {
+ if (mIsInputDroppedForAnimation == isInputDroppedForAnimation) {
+ return;
+ }
+ mIsInputDroppedForAnimation = isInputDroppedForAnimation;
+ updateUntrustedEmbeddingInputProtection();
+ }
+
/**
* Sets to drop input when obscured to activity if it is embedded in untrusted mode.
*
@@ -1659,7 +1669,10 @@
if (getSurfaceControl() == null) {
return;
}
- if (isEmbeddedInUntrustedMode()) {
+ if (mIsInputDroppedForAnimation) {
+ // Disable all input during the animation.
+ setDropInputMode(DropInputMode.ALL);
+ } else if (isEmbeddedInUntrustedMode()) {
// Set drop input to OBSCURED when untrusted embedded.
setDropInputMode(DropInputMode.OBSCURED);
} else {
@@ -2835,7 +2848,7 @@
final boolean hasImeSurface;
if (mStartingData != null) {
if (mStartingData.mWaitForSyncTransactionCommit
- || mTransitionController.isCollecting(this)) {
+ || mSyncState != SYNC_STATE_NONE) {
mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
mStartingData.mPrepareRemoveAnimation = prepareAnimation;
return;
@@ -5035,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);
@@ -5159,8 +5171,7 @@
}
if (options != null) {
- mTransitionController.setOverrideAnimation(options, this, startCallback,
- finishCallback);
+ mTransitionController.setOverrideAnimation(options, startCallback, finishCallback);
}
}
@@ -6068,9 +6079,10 @@
return false;
}
- // Check if the activity is on a sleeping display, canTurnScreenOn will also check
- // keyguard visibility
- if (mDisplayContent.isSleeping()) {
+ // Check if the activity is on a sleeping display and keyguard is not going away (to
+ // align with TaskFragment#shouldSleepActivities), canTurnScreenOn will also check keyguard
+ // visibility
+ if (mDisplayContent.isSleeping() && !mDisplayContent.isKeyguardGoingAway()) {
return canTurnScreenOn();
} else {
return mTaskSupervisor.getKeyguardController().checkKeyguardVisibility(this);
@@ -8138,6 +8150,10 @@
*/
@Override
protected int getOverrideOrientation() {
+ if (mWmService.mConstants.mIgnoreActivityOrientationRequest
+ && info.applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
+ return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ }
return mAppCompatController.getOrientationPolicy()
.overrideOrientationIfNeeded(super.getOverrideOrientation());
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 1660ca9..35ec5ad 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -424,13 +424,19 @@
Intent intent = intents[i];
NeededUriGrants intentGrants = null;
- intent.prepareToEnterSystemServer();
+ // Refuse possible leaked file descriptors.
+ if (intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
// Get the flag earlier because the intent may be modified in resolveActivity below.
final boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
intent = new Intent(intent);
+ // Remove existing mismatch flag so it can be properly updated later
+ intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i],
0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index bf18a43..1822a80 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -721,7 +721,13 @@
onExecutionStarted();
if (mRequest.intent != null) {
- mRequest.intent.prepareToEnterSystemServer();
+ // Refuse possible leaked file descriptors
+ if (mRequest.intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ // Remove existing mismatch flag so it can be properly updated later
+ mRequest.intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
final LaunchingState launchingState;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 26a6b00..3d6b64b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -130,35 +130,6 @@
}
/**
- * Sleep tokens cause the activity manager to put the top activity to sleep.
- * They are used by components such as dreams that may hide and block interaction
- * with underlying activities.
- * The Acquirer provides an interface that encapsulates the underlying work, so the user does
- * not need to handle the token by him/herself.
- */
- public interface SleepTokenAcquirer {
-
- /**
- * Acquires a sleep token.
- * @param displayId The display to apply to.
- */
- void acquire(int displayId);
-
- /**
- * Releases the sleep token.
- * @param displayId The display to apply to.
- */
- void release(int displayId);
- }
-
- /**
- * Creates a sleep token acquirer for the specified display with the specified tag.
- *
- * @param tag A string identifying the purpose (eg. "Dream").
- */
- public abstract SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag);
-
- /**
* Returns home activity for the specified user.
*
* @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
@@ -380,7 +351,16 @@
public abstract void onPackageAdded(String name, boolean replacing);
public abstract void onPackageReplaced(ApplicationInfo aInfo);
- public abstract CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai);
+ /** The data for IApplicationThread#bindApplication. */
+ public static final class PreBindInfo {
+ public final @NonNull CompatibilityInfo compatibilityInfo;
+ public final @NonNull Configuration configuration;
+
+ PreBindInfo(@NonNull CompatibilityInfo compatInfo, @NonNull Configuration config) {
+ compatibilityInfo = compatInfo;
+ configuration = config;
+ }
+ }
public final class ActivityTokens {
private final @NonNull IBinder mActivityToken;
@@ -502,7 +482,9 @@
public abstract void resumeTopActivities(boolean scheduleIdle);
/** Called by AM just before it binds to an application process. */
- public abstract void preBindApplication(WindowProcessController wpc);
+ @NonNull
+ public abstract PreBindInfo preBindApplication(@NonNull WindowProcessController wpc,
+ @NonNull ApplicationInfo info);
/** Called by AM when an application process attaches. */
public abstract boolean attachApplication(WindowProcessController wpc) throws RemoteException;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f5476f2..49ca698 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1317,7 +1317,12 @@
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
enforceNotIsolatedCaller("startActivityIntentSender");
if (fillInIntent != null) {
- fillInIntent.prepareToEnterSystemServer();
+ // Refuse possible leaked file descriptors
+ if (fillInIntent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // Remove existing mismatch flag so it can be properly updated later
+ fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
if (!(target instanceof PendingIntentRecord)) {
@@ -1343,10 +1348,10 @@
@Override
public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent,
Bundle bOptions) {
- if (intent != null) {
- intent.prepareToEnterSystemServer();
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
}
-
SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
synchronized (mGlobalLock) {
@@ -1361,6 +1366,8 @@
return false;
}
intent = new Intent(intent);
+ // Remove existing mismatch flag so it can be properly updated later
+ intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
// The caller is not allowed to change the data.
intent.setDataAndType(r.intent.getData(), r.intent.getType());
// And we are resetting to find the next component...
@@ -4349,6 +4356,7 @@
mTaskOrganizerController.dump(pw, " ");
mVisibleActivityProcessTracker.dump(pw, " ");
mActiveUids.dump(pw, " ");
+ pw.println(" SleepTokens=" + mRootWindowContainer.mSleepTokens);
if (mDemoteTopAppReasons != 0) {
pw.println(" mDemoteTopAppReasons=" + mDemoteTopAppReasons);
}
@@ -5064,17 +5072,16 @@
EventLogTags.writeWmSetResumedActivity(r.mUserId, r.shortComponentName, reason);
}
- final class SleepTokenAcquirerImpl implements ActivityTaskManagerInternal.SleepTokenAcquirer {
+ final class SleepTokenAcquirer {
private final String mTag;
private final SparseArray<RootWindowContainer.SleepToken> mSleepTokens =
new SparseArray<>();
- SleepTokenAcquirerImpl(@NonNull String tag) {
+ SleepTokenAcquirer(@NonNull String tag) {
mTag = tag;
}
- @Override
- public void acquire(int displayId) {
+ void acquire(int displayId) {
synchronized (mGlobalLock) {
if (!mSleepTokens.contains(displayId)) {
mSleepTokens.append(displayId,
@@ -5084,8 +5091,7 @@
}
}
- @Override
- public void release(int displayId) {
+ void release(int displayId) {
synchronized (mGlobalLock) {
final RootWindowContainer.SleepToken token = mSleepTokens.get(displayId);
if (token != null) {
@@ -5948,11 +5954,6 @@
}
final class LocalService extends ActivityTaskManagerInternal {
- @Override
- public SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag) {
- Objects.requireNonNull(tag);
- return new SleepTokenAcquirerImpl(tag);
- }
@Override
public ComponentName getHomeActivityForUser(int userId) {
@@ -6421,13 +6422,6 @@
}
@Override
- public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
- synchronized (mGlobalLock) {
- return compatibilityInfoForPackageLocked(ai);
- }
- }
-
- @Override
public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
int requestCode, int resultCode, Intent data) {
final ActivityRecord r;
@@ -6705,9 +6699,13 @@
@HotPath(caller = HotPath.PROCESS_CHANGE)
@Override
- public void preBindApplication(WindowProcessController wpc) {
+ public PreBindInfo preBindApplication(WindowProcessController wpc, ApplicationInfo info) {
synchronized (mGlobalLockWithoutBoost) {
mTaskSupervisor.getActivityMetricsLogger().notifyBindApplication(wpc.mInfo);
+ wpc.onConfigurationChanged(getGlobalConfiguration());
+ // The "info" can be the target of instrumentation.
+ return new PreBindInfo(compatibilityInfoForPackageLocked(info),
+ new Configuration(wpc.getConfiguration()));
}
}
@@ -7474,9 +7472,7 @@
FEATURE_LEANBACK);
final boolean isArc = arcFeature != null && arcFeature.version >= 0;
final boolean isTv = tvFeature != null && tvFeature.version >= 0;
- sIsPip2ExperimentEnabled = SystemProperties.getBoolean(
- "persist.wm_shell.pip2", false)
- || (Flags.enablePip2Implementation() && !isArc && !isTv);
+ sIsPip2ExperimentEnabled = Flags.enablePip2() && !isArc && !isTv;
}
return sIsPip2ExperimentEnabled;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e90a2c9..9a3ad2d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -107,7 +107,6 @@
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
-import android.companion.virtual.VirtualDeviceManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -158,6 +157,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.HostingRecord;
import com.android.server.am.UserState;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.pm.SaferIntentUtils;
import com.android.server.utils.Slogf;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
@@ -285,7 +285,7 @@
private WindowManagerService mWindowManager;
private AppOpsManager mAppOpsManager;
- private VirtualDeviceManager mVirtualDeviceManager;
+ private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
/** Common synchronization logic used to save things to disks. */
PersisterQueue mPersisterQueue;
@@ -1298,16 +1298,24 @@
if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
return Context.DEVICE_ID_DEFAULT;
}
- if (mVirtualDeviceManager == null) {
+ if (mVirtualDeviceManagerInternal == null) {
if (mService.mHasCompanionDeviceSetupFeature) {
- mVirtualDeviceManager =
- mService.mContext.getSystemService(VirtualDeviceManager.class);
+ mVirtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal.class);
}
- if (mVirtualDeviceManager == null) {
+ if (mVirtualDeviceManagerInternal == null) {
return Context.DEVICE_ID_DEFAULT;
}
}
- return mVirtualDeviceManager.getDeviceIdForDisplayId(displayId);
+ return mVirtualDeviceManagerInternal.getDeviceIdForDisplayId(displayId);
+ }
+
+ boolean isDeviceOwnerUid(int displayId, int callingUid) {
+ final int deviceId = getDeviceIdForDisplayId(displayId);
+ if (deviceId == Context.DEVICE_ID_DEFAULT || deviceId == Context.DEVICE_ID_INVALID) {
+ return false;
+ }
+ return mVirtualDeviceManagerInternal.getDeviceOwnerUid(deviceId) == callingUid;
}
private AppOpsManager getAppOpsManager() {
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index b23e75a..51ef87d 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -122,9 +122,12 @@
if (aspectRatioOverrides.shouldApplyUserMinAspectRatioOverride()) {
return aspectRatioOverrides.getUserMinAspectRatio();
}
+ final DisplayContent displayContent = mActivityRecord.getDisplayContent();
+ final boolean shouldOverrideMinAspectRatioForCamera = displayContent != null
+ && displayContent.mAppCompatCameraPolicy.shouldOverrideMinAspectRatioForCamera(
+ mActivityRecord);
if (!aspectRatioOverrides.shouldOverrideMinAspectRatio()
- && !mAppCompatOverrides.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera()) {
+ && !shouldOverrideMinAspectRatioForCamera) {
return info.getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index d8abf69..241390c1 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -85,9 +85,10 @@
}
/**
- * Whether we should apply the min aspect ratio per-app override only when an app is connected
- * to the camera.
- * When this override is applied the min aspect ratio given in the app's manifest will be
+ * Whether applying the min aspect ratio per-app override only when an app is connected
+ * to the camera is allowed.
+ *
+ * <p>When this override is applied the min aspect ratio given in the app's manifest will be
* overridden to the largest enabled aspect ratio treatment unless the app's manifest value
* is higher. The treatment will also apply if no value is provided in the manifest.
*
@@ -97,9 +98,8 @@
* <li>Per-app override is enabled
* </ul>
*/
- boolean shouldOverrideMinAspectRatioForCamera() {
- return isCameraActive() && mAllowMinAspectRatioOverrideOptProp
- .shouldEnableWithOptInOverrideAndOptOutProperty(
+ boolean isOverrideMinAspectRatioForCameraEnabled() {
+ return mAllowMinAspectRatioOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
isChangeEnabled(mActivityRecord,
OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA));
}
@@ -174,24 +174,6 @@
OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
}
- /**
- * @return {@code true} if the Camera is active for the current activity
- */
- boolean isCameraActive() {
- return mActivityRecord.mDisplayContent != null
- && mActivityRecord.mDisplayContent.mAppCompatCameraPolicy
- .isCameraActive(mActivityRecord, /* mustBeFullscreen */ true);
- }
-
- /**
- * @return {@code true} if the configuration needs to be recomputed after a camera state update.
- */
- boolean shouldRecomputeConfigurationForCameraCompat() {
- return isOverrideOrientationOnlyForCameraEnabled()
- || isCameraCompatSplitScreenAspectRatioAllowed()
- || shouldOverrideMinAspectRatioForCamera();
- }
-
boolean isOverrideOrientationOnlyForCameraEnabled() {
return isChangeEnabled(mActivityRecord, OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
}
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index a42b879..67bfd76 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -166,6 +166,9 @@
: SCREEN_ORIENTATION_UNSPECIFIED;
}
+ /**
+ * @return {@code true} if the Camera is active for the provided {@link ActivityRecord}.
+ */
boolean isCameraActive(@NonNull ActivityRecord activity, boolean mustBeFullscreen) {
return mDisplayRotationCompatPolicy != null
&& mDisplayRotationCompatPolicy.isCameraActive(activity, mustBeFullscreen);
@@ -179,4 +182,13 @@
return null;
}
+ /**
+ * Whether we should apply the min aspect ratio per-app override only when an app is connected
+ * to the camera.
+ */
+ boolean shouldOverrideMinAspectRatioForCamera(@NonNull ActivityRecord activityRecord) {
+ return isCameraActive(activityRecord, /* mustBeFullscreen= */ true)
+ && activityRecord.mAppCompatController.getAppCompatCameraOverrides()
+ .isOverrideMinAspectRatioForCameraEnabled();
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index c5506de..7477c62 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -58,13 +58,16 @@
&& displayContent.getIgnoreOrientationRequest();
final boolean shouldApplyUserFullscreenOverride = mAppCompatOverrides
.getAppCompatAspectRatioOverrides().shouldApplyUserFullscreenOverride();
+ final boolean isCameraActive = displayContent != null
+ && displayContent.mAppCompatCameraPolicy.isCameraActive(mActivityRecord,
+ /* mustBeFullscreen */ true);
if (shouldApplyUserFullscreenOverride && isIgnoreOrientationRequestEnabled
// Do not override orientation to fullscreen for camera activities.
// Fixed-orientation activities are rarely tested in other orientations, and it
// often results in sideways or stretched previews. As the camera compat treatment
// targets fixed-orientation activities, overriding the orientation disables the
// treatment.
- && !mAppCompatOverrides.getAppCompatCameraOverrides().isCameraActive()) {
+ && !isCameraActive) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate)
+ " for " + mActivityRecord + " is overridden to "
+ screenOrientationToString(SCREEN_ORIENTATION_USER)
@@ -110,7 +113,7 @@
// often results in sideways or stretched previews. As the camera compat treatment
// targets fixed-orientation activities, overriding the orientation disables the
// treatment.
- && !mAppCompatOverrides.getAppCompatCameraOverrides().isCameraActive()) {
+ && !isCameraActive) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate)
+ " for " + mActivityRecord + " is overridden to "
+ screenOrientationToString(SCREEN_ORIENTATION_USER));
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 197bd5a..ab02d49 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -97,6 +97,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -253,10 +254,12 @@
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
- // No AE remote animation with Shell transition.
- // Unfreeze the windows that were previously frozen for TaskFragment animation.
- unfreezeEmbeddedChangingWindows();
- overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
+ // Check if there is any override
+ if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
+ // Unfreeze the windows that were previously frozen for TaskFragment animation.
+ unfreezeEmbeddedChangingWindows();
+ overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
+ }
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mClosingApps)
|| containsVoiceInteraction(mDisplayContent.mOpeningApps);
@@ -687,6 +690,64 @@
}
/**
+ * Overrides the pending transition with the remote animation defined by the
+ * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
+ * {@link TaskFragment} that are organized by the same organizer.
+ *
+ * @return {@code true} if the transition is overridden.
+ */
+ private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
+ ArraySet<Integer> activityTypes) {
+ if (transitionMayContainNonAppWindows(transit)) {
+ return false;
+ }
+ if (!transitionContainsTaskFragmentWithBoundsOverride()) {
+ // No need to play TaskFragment remote animation if all embedded TaskFragment in the
+ // transition fill the Task.
+ return false;
+ }
+
+ final Task task = findParentTaskForAllEmbeddedWindows();
+ final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizer(task);
+ final RemoteAnimationDefinition definition = organizer != null
+ ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
+ .getRemoteAnimationDefinition(organizer)
+ : null;
+ final RemoteAnimationAdapter adapter = definition != null
+ ? definition.getAdapter(transit, activityTypes)
+ : null;
+ if (adapter == null) {
+ return false;
+ }
+ mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
+ adapter, false /* sync */, true /*isActivityEmbedding*/);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Override with TaskFragment remote animation for transit=%s",
+ AppTransition.appTransitionOldToString(transit));
+
+ final int organizerUid = mDisplayContent.mAtmService.mTaskFragmentOrganizerController
+ .getTaskFragmentOrganizerUid(organizer);
+ final boolean shouldDisableInputForRemoteAnimation = !task.isFullyTrustedEmbedding(
+ organizerUid);
+ final RemoteAnimationController remoteAnimationController =
+ mDisplayContent.mAppTransition.getRemoteAnimationController();
+ if (shouldDisableInputForRemoteAnimation && remoteAnimationController != null) {
+ // We are going to use client-driven animation, Disable all input on activity windows
+ // during the animation (unless it is fully trusted) to ensure it is safe to allow
+ // client to animate the surfaces.
+ // This is needed for all activity windows in the animation Task.
+ remoteAnimationController.setOnRemoteAnimationReady(() -> {
+ final Consumer<ActivityRecord> updateActivities =
+ activity -> activity.setDropInputForAnimation(true);
+ task.forAllActivities(updateActivities);
+ });
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "Task=%d contains embedded TaskFragment."
+ + " Disabled all input during TaskFragment remote animation.", task.mTaskId);
+ }
+ return true;
+ }
+
+ /**
* Overrides the pending transition with the remote animation defined for the transition in the
* set of defined remote animations in the app window token.
*/
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 3710f7f..0646fb7 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -339,13 +339,12 @@
removedWindowContainer,
BackNavigationInfo.typeToString(backType));
- // For now, we only animate when going home, cross task or cross-activity.
boolean prepareAnimation =
(backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
|| backType == BackNavigationInfo.TYPE_CROSS_TASK
|| backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY
|| backType == BackNavigationInfo.TYPE_DIALOG_CLOSE)
- && adapter != null;
+ && (adapter != null && adapter.isAnimatable(backType));
if (prepareAnimation) {
final AnimationHandler.ScheduleAnimationBuilder builder =
@@ -882,6 +881,7 @@
} else {
if (mAnimationHandler.mPrepareCloseTransition != null) {
Slog.e(TAG, "Gesture animation is applied on another transition?");
+ return;
}
mAnimationHandler.mPrepareCloseTransition = transition;
if (!migratePredictToTransition) {
@@ -1072,8 +1072,10 @@
return close.asWindowState() != null;
}
- private void initiate(@NonNull WindowContainer close, @NonNull WindowContainer[] open,
+ private void initiate(ScheduleAnimationBuilder builder,
@NonNull ActivityRecord[] openingActivities) {
+ WindowContainer close = builder.mCloseTarget;
+ WindowContainer[] open = builder.mOpenTargets;
if (isActivitySwitch(close, open)) {
mSwitchType = ACTIVITY_SWITCH;
if (Flags.migratePredictiveBackTransition()) {
@@ -1091,9 +1093,17 @@
return;
}
- mCloseAdaptor = createAdaptor(close, false, mSwitchType);
+ final Transition prepareTransition = builder.prepareTransitionIfNeeded(
+ openingActivities);
+ final SurfaceControl.Transaction st = openingActivities[0].getSyncTransaction();
+ final SurfaceControl.Transaction ct = prepareTransition != null
+ ? st : close.getPendingTransaction();
+ mCloseAdaptor = createAdaptor(close, false, mSwitchType, ct);
if (mCloseAdaptor.mAnimationTarget == null) {
Slog.w(TAG, "composeNewAnimations fail, skip");
+ if (prepareTransition != null) {
+ prepareTransition.abort();
+ }
clearBackAnimateTarget(true /* cancel */);
return;
}
@@ -1110,12 +1120,17 @@
next.getWindowConfiguration().getRotation());
}
}
- mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(true, mSwitchType, open);
+ mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(
+ true, mSwitchType, st, open);
if (!mOpenAnimAdaptor.isValid()) {
Slog.w(TAG, "compose animations fail, skip");
+ if (prepareTransition != null) {
+ prepareTransition.abort();
+ }
clearBackAnimateTarget(true /* cancel */);
return;
}
+ mOpenAnimAdaptor.mPreparedOpenTransition = prepareTransition;
mOpenActivities = openingActivities;
}
@@ -1147,19 +1162,21 @@
return new Pair<>(replaceClose, replaceOpen);
}
- private boolean composeAnimations(@NonNull WindowContainer close,
- @NonNull WindowContainer[] open, @NonNull ActivityRecord[] openingActivities) {
+ private boolean composeAnimations(@NonNull ScheduleAnimationBuilder builder,
+ @NonNull ActivityRecord[] openingActivities) {
if (mComposed || mWaitTransition) {
Slog.e(TAG, "Previous animation is running " + this);
return false;
}
clearBackAnimateTarget(true /* cancel */);
- if (close == null || open == null || open.length == 0 || open.length > 2) {
+ final WindowContainer[] open = builder.mOpenTargets;
+ if (builder.mCloseTarget == null || open == null || open.length == 0
+ || open.length > 2) {
Slog.e(TAG, "reset animation with null target close: "
- + close + " open: " + Arrays.toString(open));
+ + builder.mCloseTarget + " open: " + Arrays.toString(open));
return false;
}
- initiate(close, open, openingActivities);
+ initiate(builder, openingActivities);
if (mSwitchType == UNKNOWN) {
return false;
}
@@ -1384,10 +1401,10 @@
}
@NonNull private static BackWindowAnimationAdaptor createAdaptor(
- @NonNull WindowContainer target, boolean isOpen, int switchType) {
+ @NonNull WindowContainer target, boolean isOpen, int switchType,
+ SurfaceControl.Transaction st) {
final BackWindowAnimationAdaptor adaptor =
new BackWindowAnimationAdaptor(target, isOpen, switchType);
- final SurfaceControl.Transaction pt = target.getPendingTransaction();
// Workaround to show TaskFragment which can be hide in Transitions and won't show
// during isAnimating.
if (isOpen && target.asActivityRecord() != null) {
@@ -1395,10 +1412,10 @@
if (fragment != null) {
// Ensure task fragment surface has updated, in case configuration has changed.
fragment.updateOrganizedTaskFragmentSurface();
- pt.show(fragment.mSurfaceControl);
+ st.show(fragment.mSurfaceControl);
}
}
- target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK);
+ target.startAnimation(st, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK);
return adaptor;
}
@@ -1417,12 +1434,12 @@
private Transition mPreparedOpenTransition;
BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType,
- @NonNull WindowContainer... targets) {
+ SurfaceControl.Transaction st, @NonNull WindowContainer... targets) {
mAdaptors = new BackWindowAnimationAdaptor[targets.length];
for (int i = targets.length - 1; i >= 0; --i) {
- mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType);
+ mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType, st);
}
- mRemoteAnimationTarget = targets.length > 1 ? createWrapTarget()
+ mRemoteAnimationTarget = targets.length > 1 ? createWrapTarget(st)
: mAdaptors[0].mAnimationTarget;
}
@@ -1448,7 +1465,7 @@
mPreparedOpenTransition = null;
}
- private RemoteAnimationTarget createWrapTarget() {
+ private RemoteAnimationTarget createWrapTarget(SurfaceControl.Transaction st) {
// Special handle for opening two activities together.
// If we animate both activities separately, the animation area and rounded corner
// would also being handled separately. To make them seem like "open" together, wrap
@@ -1470,12 +1487,11 @@
.build();
mCloseTransaction = new SurfaceControl.Transaction();
mCloseTransaction.reparent(leashSurface, null);
- final SurfaceControl.Transaction pt = wc.getPendingTransaction();
- pt.setLayer(leashSurface, wc.getLastLayer());
+ st.setLayer(leashSurface, wc.getLastLayer());
for (int i = mAdaptors.length - 1; i >= 0; --i) {
BackWindowAnimationAdaptor adaptor = mAdaptors[i];
- pt.reparent(adaptor.mAnimationTarget.leash, leashSurface);
- pt.setPosition(adaptor.mAnimationTarget.leash,
+ st.reparent(adaptor.mAnimationTarget.leash, leashSurface);
+ st.setPosition(adaptor.mAnimationTarget.leash,
adaptor.mAnimationTarget.localBounds.left,
adaptor.mAnimationTarget.localBounds.top);
// For adjacent activity embedded, reparent Activity to TaskFragment when
@@ -1738,6 +1754,7 @@
WindowContainer mCloseTarget;
WindowContainer[] mOpenTargets;
boolean mIsLaunchBehind;
+ TaskSnapshot mSnapshot;
ScheduleAnimationBuilder(int type, BackAnimationAdapter adapter,
NavigationMonitor monitor) {
@@ -1771,6 +1788,13 @@
return wc == mCloseTarget || mCloseTarget.hasChild(wc) || wc.hasChild(mCloseTarget);
}
+ private Transition prepareTransitionIfNeeded(ActivityRecord[] visibleOpenActivities) {
+ if (mSnapshot == null) {
+ return setLaunchBehind(visibleOpenActivities);
+ }
+ return null;
+ }
+
/**
* Apply preview strategy on the opening target
*
@@ -1780,26 +1804,17 @@
private void applyPreviewStrategy(
@NonNull BackWindowAnimationAdaptorWrapper openAnimationAdaptor,
@NonNull ActivityRecord[] visibleOpenActivities) {
- boolean needsLaunchBehind = true;
if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
boolean activitiesAreDrawn = false;
for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
// If the activity hasn't stopped, it's window should remain drawn.
activitiesAreDrawn |= visibleOpenActivities[i].firstWindowDrawn;
}
- final WindowContainer mainOpen = openAnimationAdaptor.mAdaptors[0].mTarget;
- final TaskSnapshot snapshot = getSnapshot(mainOpen, visibleOpenActivities);
// Don't create starting surface if previous activities haven't stopped or
// the snapshot does not exist.
- if (snapshot != null || !activitiesAreDrawn) {
- openAnimationAdaptor.createStartingSurface(snapshot);
+ if (mSnapshot != null || !activitiesAreDrawn) {
+ openAnimationAdaptor.createStartingSurface(mSnapshot);
}
- // Only use LaunchBehind if snapshot does not exist.
- needsLaunchBehind = snapshot == null;
- }
- if (needsLaunchBehind) {
- openAnimationAdaptor.mPreparedOpenTransition =
- setLaunchBehind(visibleOpenActivities);
}
// Force update mLastSurfaceShowing for opening activity and its task.
if (mWindowManagerService.mRoot.mTransitionController.isShellTransitionsEnabled()) {
@@ -1821,7 +1836,11 @@
return null;
}
- if (!composeAnimations(mCloseTarget, mOpenTargets, openingActivities)) {
+ if (!shouldLaunchBehind && mShowWindowlessSurface) {
+ mSnapshot = getSnapshot(mOpenTargets[0], openingActivities);
+ }
+
+ if (!composeAnimations(this, openingActivities)) {
return null;
}
mCloseTarget.mTransitionController.mSnapshotController
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 20c5f02..2259b5a 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -21,10 +21,10 @@
import static android.app.ActivityOptions.BackgroundActivityStartMode;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
-import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -39,13 +39,15 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
+import static com.android.window.flags.Flags.balAdditionalStartModes;
import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
-import static com.android.window.flags.Flags.balImprovedMetrics;
import static com.android.window.flags.Flags.balImproveRealCallerVisibilityCheck;
+import static com.android.window.flags.Flags.balImprovedMetrics;
import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
import static com.android.window.flags.Flags.balRequireOptInSameUid;
import static com.android.window.flags.Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid;
@@ -84,6 +86,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.UiThread;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
import java.lang.annotation.Retention;
import java.util.ArrayList;
@@ -107,6 +110,17 @@
private static final int ASM_GRACEPERIOD_MAX_REPEATS = 5;
private static final int NO_PROCESS_UID = -1;
+ private static final BalCheckConfiguration BAL_CHECK_FOREGROUND = new BalCheckConfiguration(
+ /* isCheckingForFgsStarts */ false,
+ /* checkVisibility */ true,
+ /* checkOtherExemptions */ false,
+ ACTIVITY_BG_START_GRACE_PERIOD_MS);
+ private static final BalCheckConfiguration BAL_CHECK_BACKGROUND = new BalCheckConfiguration(
+ /* isCheckingForFgsStarts */ false,
+ /* checkVisibility */ false,
+ /* checkOtherExemptions */ true,
+ ACTIVITY_BG_START_GRACE_PERIOD_MS);
+
static final String AUTO_OPT_IN_NOT_PENDING_INTENT = "notPendingIntent";
static final String AUTO_OPT_IN_CALL_FOR_RESULT = "callForResult";
static final String AUTO_OPT_IN_SAME_UID = "sameUid";
@@ -412,6 +426,8 @@
int callingUid, String callingPackage, ActivityOptions checkedOptions) {
switch (checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()) {
case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+ case MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE:
+ case MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS:
return BackgroundStartPrivileges.ALLOW_BAL;
case MODE_BACKGROUND_ACTIVITY_START_DENIED:
return BackgroundStartPrivileges.NONE;
@@ -752,7 +768,7 @@
// PendingIntents is null).
BalVerdict resultForRealCaller = state.callerIsRealCaller() && resultForCaller.allows()
? resultForCaller
- : checkBackgroundActivityStartAllowedBySender(state)
+ : checkBackgroundActivityStartAllowedByRealCaller(state)
.setBasedOnRealCaller();
state.setResultForRealCaller(resultForRealCaller);
@@ -827,6 +843,37 @@
* or {@link #BAL_BLOCK} if the launch should be blocked
*/
BalVerdict checkBackgroundActivityStartAllowedByCaller(BalState state) {
+ if (state.isPendingIntent()) {
+ // PendingIntents should mostly be allowed by the sender (real caller) or a permission
+ // the creator of the PendingIntent has. Visibility should be the exceptional case, so
+ // test it last (this does not change the result, just the bal code).
+ BalVerdict result = BalVerdict.BLOCK;
+ if (!(balAdditionalStartModes()
+ && state.mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+ result = checkBackgroundActivityStartAllowedByCallerInBackground(state);
+ }
+ if (result == BalVerdict.BLOCK) {
+ result = checkBackgroundActivityStartAllowedByCallerInForeground(state);
+
+ }
+ return result;
+ } else {
+ BalVerdict result = checkBackgroundActivityStartAllowedByCallerInForeground(state);
+ if (result == BalVerdict.BLOCK && !(balAdditionalStartModes()
+ && state.mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+ result = checkBackgroundActivityStartAllowedByCallerInBackground(state);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * @return A code denoting which BAL rule allows an activity to be started,
+ * or {@link #BAL_BLOCK} if the launch should be blocked
+ */
+ BalVerdict checkBackgroundActivityStartAllowedByCallerInForeground(BalState state) {
// This is used to block background activity launch even if the app is still
// visible to user after user clicking home button.
@@ -842,7 +889,16 @@
return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
/*background*/ false, "callingUid has non-app visible window");
}
+ // Don't abort if the callerApp or other processes of that uid are considered to be in the
+ // foreground.
+ return checkProcessAllowsBal(state.mCallerApp, state, BAL_CHECK_FOREGROUND);
+ }
+ /**
+ * @return A code denoting which BAL rule allows an activity to be started,
+ * or {@link #BAL_BLOCK} if the launch should be blocked
+ */
+ BalVerdict checkBackgroundActivityStartAllowedByCallerInBackground(BalState state) {
// don't abort for the most important UIDs
final int callingAppId = UserHandle.getAppId(state.mCallingUid);
if (state.mCallingUid == Process.ROOT_UID
@@ -922,25 +978,29 @@
"OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop is granted");
}
- // If we don't have callerApp at this point, no caller was provided to startActivity().
- // That's the case for PendingIntent-based starts, since the creator's process might not be
- // up and alive.
// Don't abort if the callerApp or other processes of that uid are allowed in any way.
- BalVerdict callerAppAllowsBal = checkProcessAllowsBal(state.mCallerApp, state);
- if (callerAppAllowsBal.allows()) {
- return callerAppAllowsBal;
- }
-
- // If we are here, it means all exemptions based on the creator failed
- return BalVerdict.BLOCK;
+ return checkProcessAllowsBal(state.mCallerApp, state, BAL_CHECK_BACKGROUND);
}
/**
* @return A code denoting which BAL rule allows an activity to be started,
* or {@link #BAL_BLOCK} if the launch should be blocked
*/
- BalVerdict checkBackgroundActivityStartAllowedBySender(BalState state) {
+ BalVerdict checkBackgroundActivityStartAllowedByRealCaller(BalState state) {
+ BalVerdict result = checkBackgroundActivityStartAllowedByRealCallerInForeground(state);
+ if (result == BalVerdict.BLOCK && !(balAdditionalStartModes()
+ && state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+ result = checkBackgroundActivityStartAllowedByRealCallerInBackground(state);
+ }
+ return result;
+ }
+ /**
+ * @return A code denoting which BAL rule allows an activity to be started,
+ * or {@link #BAL_BLOCK} if the launch should be blocked
+ */
+ BalVerdict checkBackgroundActivityStartAllowedByRealCallerInForeground(BalState state) {
// Normal apps with visible app window will be allowed to start activity if app switching
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
// The home app can start apps even if app switches are usually disallowed.
@@ -966,6 +1026,16 @@
}
}
+ // Don't abort if the realCallerApp or other processes of that uid are considered to be in
+ // the foreground.
+ return checkProcessAllowsBal(state.mRealCallerApp, state, BAL_CHECK_FOREGROUND);
+ }
+
+ /**
+ * @return A code denoting which BAL rule allows an activity to be started,
+ * or {@link #BAL_BLOCK} if the launch should be blocked
+ */
+ BalVerdict checkBackgroundActivityStartAllowedByRealCallerInBackground(BalState state) {
if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
== MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
&& hasBalPermission(state.mRealCallingUid, state.mRealCallingPid)) {
@@ -992,14 +1062,7 @@
}
// don't abort if the callerApp or other processes of that uid are allowed in any way
- BalVerdict realCallerAppAllowsBal =
- checkProcessAllowsBal(state.mRealCallerApp, state);
- if (realCallerAppAllowsBal.allows()) {
- return realCallerAppAllowsBal;
- }
-
- // If we are here, it means all exemptions based on PI sender failed
- return BalVerdict.BLOCK;
+ return checkProcessAllowsBal(state.mRealCallerApp, state, BAL_CHECK_BACKGROUND);
}
@VisibleForTesting boolean hasBalPermission(int uid, int pid) {
@@ -1015,13 +1078,13 @@
* exceptions.
*/
@VisibleForTesting BalVerdict checkProcessAllowsBal(WindowProcessController app,
- BalState state) {
+ BalState state, BalCheckConfiguration balCheckConfiguration) {
if (app == null) {
return BalVerdict.BLOCK;
}
// first check the original calling process
final BalVerdict balAllowedForCaller = app
- .areBackgroundActivityStartsAllowed(state.mAppSwitchState);
+ .areBackgroundActivityStartsAllowed(state.mAppSwitchState, balCheckConfiguration);
if (balAllowedForCaller.allows()) {
return balAllowedForCaller.withProcessInfo("callerApp process", app);
} else {
@@ -1033,7 +1096,7 @@
final WindowProcessController proc = uidProcesses.valueAt(i);
if (proc != app) {
BalVerdict balAllowedForUid = proc.areBackgroundActivityStartsAllowed(
- state.mAppSwitchState);
+ state.mAppSwitchState, balCheckConfiguration);
if (balAllowedForUid.allows()) {
return balAllowedForUid.withProcessInfo("process", proc);
}
@@ -1685,6 +1748,21 @@
(state.mOriginatingPendingIntent != null));
}
+ if (finalVerdict.getRawCode() == BAL_ALLOW_GRACE_PERIOD) {
+ if (state.realCallerExplicitOptInOrAutoOptIn()
+ && state.mResultForRealCaller.allows()
+ && state.mResultForRealCaller.getRawCode() != BAL_ALLOW_GRACE_PERIOD) {
+ // real caller could allow with a different exemption
+ } else if (state.callerExplicitOptInOrAutoOptIn() && state.mResultForCaller.allows()
+ && state.mResultForCaller.getRawCode() != BAL_ALLOW_GRACE_PERIOD) {
+ // caller could allow with a different exemption
+ } else {
+ // log to determine grace period length distribution
+ Slog.wtf(TAG, "Activity start ONLY allowed by BAL_ALLOW_GRACE_PERIOD "
+ + finalVerdict.mMessage + ": " + state);
+ }
+ }
+
if (balImprovedMetrics()) {
if (shouldLogStats(finalVerdict, state)) {
String activityName;
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 4a870a3..1073713 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
@@ -48,7 +47,6 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.IntArray;
-import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
@@ -100,60 +98,75 @@
mBackgroundActivityStartCallback = callback;
}
+ record BalCheckConfiguration(
+ boolean isCheckingForFgsStart,
+ boolean checkVisibility,
+ boolean checkOtherExemptions,
+ long gracePeriod
+ ) {
+ }
+
+ /**
+ * Check configuration for foreground service starts.
+ *
+ * The check executes all parts of the BAL checks and uses the same grace period,
+ * so FGS is allowed whenever BAL is allowed.
+ */
+ static final BalCheckConfiguration CHECK_FOR_FGS_START = new BalCheckConfiguration(
+ /* isCheckingForFgsStarts */ true,
+ /* checkVisibility */ true,
+ /* checkOtherExemptions */ true,
+ ACTIVITY_BG_START_GRACE_PERIOD_MS);
+
BalVerdict areBackgroundActivityStartsAllowed(
int pid, int uid, String packageName,
- int appSwitchState, boolean isCheckingForFgsStart,
+ int appSwitchState, BalCheckConfiguration checkConfiguration,
boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
long lastStopAppSwitchesTime, long lastActivityLaunchTime,
long lastActivityFinishTime) {
// Allow if the proc is instrumenting with background activity starts privs.
- if (hasBackgroundActivityStartPrivileges) {
+ if (checkConfiguration.checkOtherExemptions && hasBackgroundActivityStartPrivileges) {
return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
"process instrumenting with background activity starts privileges");
}
// Allow if the flag was explicitly set.
- if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
+ if (checkConfiguration.checkOtherExemptions && isBackgroundStartAllowedByToken(uid,
+ packageName, checkConfiguration.isCheckingForFgsStart)) {
return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_TOKEN : BAL_ALLOW_PERMISSION,
/*background*/ true, "process allowed by token");
}
// Allow if the caller is bound by a UID that's currently foreground.
// But still respect the appSwitchState.
- boolean allowBoundByForegroundUid =
+ if (checkConfiguration.checkVisibility && (
Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid()
- ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid()
- : isBoundByForegroundUid();
- if (allowBoundByForegroundUid) {
+ ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid()
+ : isBoundByForegroundUid())) {
return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_BOUND_BY_FOREGROUND
: BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false,
"process bound by foreground uid");
}
// Allow if the caller has an activity in any foreground task.
- if (hasActivityInVisibleTask && appSwitchState != APP_SWITCH_DISALLOW) {
+ if (checkConfiguration.checkVisibility && hasActivityInVisibleTask
+ && appSwitchState != APP_SWITCH_DISALLOW) {
return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ false,
"process has activity in foreground task");
}
// If app switching is not allowed, we ignore all the start activity grace period
// exception so apps cannot start itself in onPause() after pressing home button.
- if (appSwitchState == APP_SWITCH_ALLOW) {
+ if (checkConfiguration.checkOtherExemptions && appSwitchState == APP_SWITCH_ALLOW) {
// Allow if any activity in the caller has either started or finished very recently, and
// it must be started or finished after last stop app switches time.
- final long now = SystemClock.uptimeMillis();
- if (now - lastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
- || now - lastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
- // If activity is started and finished before stop app switch time, we should not
- // let app to be able to start background activity even it's in grace period.
- if (lastActivityLaunchTime > lastStopAppSwitchesTime
- || lastActivityFinishTime > lastStopAppSwitchesTime) {
+ if (lastActivityLaunchTime > lastStopAppSwitchesTime
+ || lastActivityFinishTime > lastStopAppSwitchesTime) {
+ final long now = SystemClock.uptimeMillis();
+ long timeSinceLastStartOrFinish = now - Math.max(lastActivityLaunchTime,
+ lastActivityFinishTime);
+ if (timeSinceLastStartOrFinish < checkConfiguration.gracePeriod) {
return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ true,
- "within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
+ "within " + checkConfiguration.gracePeriod + "ms grace period ("
+ + timeSinceLastStartOrFinish + "ms)");
}
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[Process(" + pid + ")] Activity start within "
- + ACTIVITY_BG_START_GRACE_PERIOD_MS
- + "ms grace period but also within stop app switch window");
- }
-
}
}
return BalVerdict.BLOCK;
diff --git a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
index ff1742b..1924691 100644
--- a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
@@ -198,9 +198,11 @@
return aspectRatioOverrides.getUserMinAspectRatio();
}
+ final DisplayContent dc = task.mDisplayContent;
+ final boolean shouldOverrideMinAspectRatioForCamera = dc != null
+ && dc.mAppCompatCameraPolicy.shouldOverrideMinAspectRatioForCamera(mActivityRecord);
if (!aspectRatioOverrides.shouldOverrideMinAspectRatio()
- && !mAppCompatOverrides.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera()) {
+ && !shouldOverrideMinAspectRatioForCamera) {
return info.getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 2d1eb41..c5643ea 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -55,7 +55,7 @@
SurfaceControl mDimSurface;
final WindowContainer<?> mHostContainer;
// The last container to request to dim
- private WindowContainer<?> mLastRequestedDimContainer;
+ private WindowState mLastDimmingWindow;
/** Animation */
private final DimmerAnimationHelper mAnimationHelper;
boolean mSkipAnimation = false;
@@ -129,8 +129,8 @@
* Set the parameters to prepare the dim to be relative parented to the dimming container
*/
void prepareReparent(@NonNull WindowContainer<?> geometryParent,
- @NonNull WindowContainer<?> relativeParent, int relativeLayer) {
- mAnimationHelper.setRequestedRelativeParent(relativeParent, relativeLayer);
+ @NonNull WindowState relativeParent) {
+ mAnimationHelper.setRequestedRelativeParent(relativeParent);
mAnimationHelper.setRequestedGeometryParent(geometryParent);
}
@@ -146,7 +146,7 @@
* Whether anyone is currently requesting the dim
*/
boolean isDimming() {
- return mLastRequestedDimContainer != null
+ return mLastDimmingWindow != null
&& (mHostContainer.isVisibleRequested() || !Flags.useTasksDimOnly());
}
@@ -186,7 +186,7 @@
*/
void resetDimStates() {
if (mDimState != null) {
- mDimState.mLastRequestedDimContainer = null;
+ mDimState.mLastDimmingWindow = null;
}
}
@@ -200,7 +200,7 @@
* @param alpha Dim amount
* @param blurRadius Blur amount
*/
- protected void adjustAppearance(@NonNull WindowContainer<?> dimmingContainer,
+ protected void adjustAppearance(@NonNull WindowState dimmingContainer,
float alpha, int blurRadius) {
final DimState d = obtainDimState(dimmingContainer);
d.prepareLookChange(alpha, blurRadius);
@@ -218,14 +218,13 @@
* continue dimming. Indeed, this method won't be able to keep dimming or get a new DimState
* without also adjusting the appearance.
* @param geometryParent The container that defines the geometry of the dim
- * @param dimmingContainer The container which to dim above. Should be a child of the host.
- * @param relativeLayer The position of the dim wrt the container
+ * @param dimmingContainer The container that is dimming. The dim layer will be rel-z
+ * parented below it
*/
public void adjustPosition(@NonNull WindowContainer<?> geometryParent,
- @NonNull WindowContainer<?> dimmingContainer,
- int relativeLayer) {
+ @NonNull WindowState dimmingContainer) {
if (mDimState != null) {
- mDimState.prepareReparent(geometryParent, dimmingContainer, relativeLayer);
+ mDimState.prepareReparent(geometryParent, dimmingContainer);
}
}
@@ -250,9 +249,9 @@
if (!Flags.useTasksDimOnly()) {
mDimState.adjustSurfaceLayout(t);
}
- final WindowState ws = mDimState.mLastRequestedDimContainer.asWindowState();
- if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
- && ws.mActivityRecord.mStartingData != null) {
+ if (!mDimState.mIsVisible && mDimState.mLastDimmingWindow != null
+ && mDimState.mLastDimmingWindow.mActivityRecord != null
+ && mDimState.mLastDimmingWindow.mActivityRecord.mStartingData != null) {
// Skip enter animation while starting window is on top of its activity
mDimState.mSkipAnimation = true;
}
@@ -262,11 +261,11 @@
}
@NonNull
- private DimState obtainDimState(@NonNull WindowContainer<?> container) {
+ private DimState obtainDimState(@NonNull WindowState window) {
if (mDimState == null) {
mDimState = new DimState();
}
- mDimState.mLastRequestedDimContainer = container;
+ mDimState.mLastDimmingWindow = window;
return mDimState;
}
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
index 4abf806..faf6dc6 100644
--- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -48,9 +48,8 @@
static class Change {
private float mAlpha = -1f;
private int mBlurRadius = -1;
- private WindowContainer<?> mDimmingContainer = null;
+ private WindowState mDimmingContainer = null;
private WindowContainer<?> mGeometryParent = null;
- private int mRelativeLayer = -1;
private static final float EPSILON = 0.0001f;
Change() {}
@@ -64,7 +63,6 @@
mBlurRadius = other.mBlurRadius;
mDimmingContainer = other.mDimmingContainer;
mGeometryParent = other.mGeometryParent;
- mRelativeLayer = other.mRelativeLayer;
}
// Same alpha and blur
@@ -84,7 +82,7 @@
@Override
public String toString() {
return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
- + mDimmingContainer + ", relativePosition=" + mRelativeLayer;
+ + mDimmingContainer + ", geometryParent " + mGeometryParent;
}
}
@@ -100,14 +98,13 @@
}
void setExitParameters() {
- setRequestedRelativeParent(mRequestedProperties.mDimmingContainer, -1 /* relativeLayer */);
+ setRequestedRelativeParent(mRequestedProperties.mDimmingContainer);
setRequestedAppearance(0f /* alpha */, 0 /* blur */);
}
// Sets a requested change without applying it immediately
- void setRequestedRelativeParent(@NonNull WindowContainer<?> relativeParent, int relativeLayer) {
+ void setRequestedRelativeParent(@NonNull WindowState relativeParent) {
mRequestedProperties.mDimmingContainer = relativeParent;
- mRequestedProperties.mRelativeLayer = relativeLayer;
}
// Sets the requested layer to reparent the dim to without applying it immediately
@@ -124,7 +121,7 @@
/**
* Commit the last changes we received. Called after
* {@link Change#setExitParameters()},
- * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
+ * {@link Change#setRequestedRelativeParent(WindowContainer)}, or
* {@link Change#setRequestedAppearance(float, int)}
*/
void applyChanges(@NonNull SurfaceControl.Transaction t, @NonNull Dimmer.DimState dim) {
@@ -147,8 +144,8 @@
reparent(dim.mDimSurface,
startProperties.mGeometryParent != mRequestedProperties.mGeometryParent
? mRequestedProperties.mGeometryParent.getSurfaceControl() : null,
- mRequestedProperties.mDimmingContainer.getSurfaceControl(),
- mRequestedProperties.mRelativeLayer, t);
+ mRequestedProperties.mDimmingContainer != startProperties.mDimmingContainer
+ ? mRequestedProperties.mDimmingContainer.getSurfaceControl() : null, t);
if (!startProperties.hasSameVisualProperties(mRequestedProperties)) {
stopCurrentAnimation(dim.mDimSurface);
@@ -187,9 +184,11 @@
mLocalAnimationAdapter.startAnimation(dim.mDimSurface, t,
ANIMATION_TYPE_DIMMER, /* finishCallback */ (type, animator) -> {
synchronized (dim.mHostContainer.mWmService.mGlobalLock) {
- setCurrentAlphaBlur(dim.mDimSurface, t);
+ SurfaceControl.Transaction finishTransaction =
+ dim.mHostContainer.getSyncTransaction();
+ setCurrentAlphaBlur(dim.mDimSurface, finishTransaction);
if (targetAlpha == 0f && !dim.isDimming()) {
- dim.remove(t);
+ dim.remove(finishTransaction);
}
mLocalAnimationAdapter = null;
mAlphaAnimationSpec = null;
@@ -232,14 +231,15 @@
*/
static void reparent(@NonNull SurfaceControl dimLayer,
@Nullable SurfaceControl newGeometryParent,
- @NonNull SurfaceControl relativeParent,
- int relativePosition,
+ @Nullable SurfaceControl newRelativeParent,
@NonNull SurfaceControl.Transaction t) {
try {
if (newGeometryParent != null) {
t.reparent(dimLayer, newGeometryParent);
}
- t.setRelativeLayer(dimLayer, relativeParent, relativePosition);
+ if (newRelativeParent != null) {
+ t.setRelativeLayer(dimLayer, newRelativeParent, -1);
+ }
} catch (NullPointerException e) {
Log.w(TAG, "Tried to change parent of dim " + dimLayer + " after remove", e);
}
@@ -256,10 +256,13 @@
private static long getDimDuration(@NonNull WindowContainer<?> container) {
// Use the same duration as the animation on the WindowContainer
- AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
- final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
- return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION_MS * durationScale)
- : animationAdapter.getDurationHint();
+ if (container.mSurfaceAnimator != null) {
+ AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
+ final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
+ return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION_MS * durationScale)
+ : animationAdapter.getDurationHint();
+ }
+ return 0;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0597ed7..10e0641 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -235,7 +235,6 @@
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceSession;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager;
@@ -572,8 +571,6 @@
boolean mWallpaperMayChange = false;
- private final SurfaceSession mSession = new SurfaceSession();
-
/**
* A perf hint session which will boost the refresh rate for the display and change sf duration
* to handle larger workloads.
@@ -738,8 +735,6 @@
/** All tokens used to put activities on this root task to sleep (including mOffToken) */
final ArrayList<RootWindowContainer.SleepToken> mAllSleepTokens = new ArrayList<>();
- /** The token acquirer to put root tasks on the display to sleep */
- private final ActivityTaskManagerInternal.SleepTokenAcquirer mOffTokenAcquirer;
private boolean mSleeping;
@@ -1134,7 +1129,6 @@
mDisplay = display;
mDisplayId = display.getDisplayId();
mCurrentUniqueDisplayId = display.getUniqueId();
- mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
mWallpaperController = new WallpaperController(mWmService, this);
mWallpaperController.resetLargestDisplay(display);
display.getDisplayInfo(mDisplayInfo);
@@ -1284,7 +1278,7 @@
* @param transaction as part of which to perform the configuration
*/
private void configureSurfaces(Transaction transaction) {
- final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
+ final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder()
.setOpaque(true)
.setContainerLayer()
.setCallsite("DisplayContent");
@@ -1835,7 +1829,7 @@
if (mTransitionController.useShellTransitionsRotation()) {
return ROTATION_UNDEFINED;
}
- final int activityOrientation = r.getOverrideOrientation();
+ int activityOrientation = r.getOverrideOrientation();
if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM
|| shouldIgnoreOrientationRequest(activityOrientation)) {
return ROTATION_UNDEFINED;
@@ -1846,14 +1840,15 @@
r /* boundary */, false /* includeBoundary */, true /* traverseTopToBottom */);
if (nextCandidate != null) {
r = nextCandidate;
+ activityOrientation = r.getOverrideOrientation();
}
}
- if (r.inMultiWindowMode() || r.getRequestedConfigurationOrientation(true /* forDisplay */)
- == getConfiguration().orientation) {
+ if (r.inMultiWindowMode() || r.getRequestedConfigurationOrientation(true /* forDisplay */,
+ activityOrientation) == getConfiguration().orientation) {
return ROTATION_UNDEFINED;
}
final int currentRotation = getRotation();
- final int rotation = mDisplayRotation.rotationForOrientation(r.getRequestedOrientation(),
+ final int rotation = mDisplayRotation.rotationForOrientation(activityOrientation,
currentRotation);
if (rotation == currentRotation) {
return ROTATION_UNDEFINED;
@@ -3214,22 +3209,21 @@
* If xPpi or yDpi is equal to {@link #INVALID_DPI}, the values are ignored.
*/
void setForcedSize(int width, int height, float xDPI, float yDPI) {
- // Can't force size higher than the maximal allowed
- if (mMaxUiWidth > 0 && width > mMaxUiWidth) {
- final float ratio = mMaxUiWidth / (float) width;
- height = (int) (height * ratio);
- width = mMaxUiWidth;
- }
-
mIsSizeForced = mInitialDisplayWidth != width || mInitialDisplayHeight != height;
if (mIsSizeForced) {
+ if (mMaxUiWidth > 0 && width > mMaxUiWidth) {
+ final float ratio = mMaxUiWidth / (float) width;
+ height = (int) (height * ratio);
+ width = mMaxUiWidth;
+ }
final Point size = getValidForcedSize(width, height);
width = size.x;
height = size.y;
}
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();
@@ -4596,7 +4590,7 @@
// removed on the task.
removeImeSurfaceImmediately();
mImeScreenshot = new ImeScreenshot(
- mWmService.mSurfaceControlFactory.apply(null), imeTarget);
+ mWmService.mSurfaceControlFactory.get(), imeTarget);
// If the caller requests to hide IME, then allow to show IME snapshot for any target task.
// So IME won't look like suddenly disappeared. It usually happens when turning off screen.
mImeScreenshot.attachAndShow(t, hideImeWindow /* anyTargetTask */);
@@ -5429,14 +5423,8 @@
}
@Override
- SurfaceSession getSession() {
- return mSession;
- }
-
- @Override
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
- SurfaceSession s = child != null ? child.getSession() : getSession();
- final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
+ final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder().setContainerLayer();
if (child == null) {
return b;
}
@@ -5452,12 +5440,12 @@
* and other potpourii.
*/
SurfaceControl.Builder makeOverlay() {
- return mWmService.makeSurfaceBuilder(mSession).setParent(getOverlayLayer());
+ return mWmService.makeSurfaceBuilder().setParent(getOverlayLayer());
}
@Override
public SurfaceControl.Builder makeAnimationLeash() {
- return mWmService.makeSurfaceBuilder(mSession).setParent(mSurfaceControl)
+ return mWmService.makeSurfaceBuilder().setParent(mSurfaceControl)
.setContainerLayer();
}
@@ -6166,9 +6154,9 @@
final int displayState = mDisplayInfo.state;
if (displayId != DEFAULT_DISPLAY) {
if (displayState == Display.STATE_OFF) {
- mOffTokenAcquirer.acquire(mDisplayId);
+ mRootWindowContainer.mDisplayOffTokenAcquirer.acquire(mDisplayId);
} else if (displayState == Display.STATE_ON) {
- mOffTokenAcquirer.release(mDisplayId);
+ mRootWindowContainer.mDisplayOffTokenAcquirer.release(mDisplayId);
}
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Display %d state was (%d), is now (%d), so update "
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 745b792..0fa1a21 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -804,6 +804,14 @@
mAwake /* waiting */);
if (!awake) {
onDisplaySwitchFinished();
+ // In case PhoneWindowManager's startedGoingToSleep is called after screenTurnedOff
+ // (the source caller is in order but the methods run on different threads) and
+ // updateScreenOffSleepToken was skipped by mIsGoingToSleepDefaultDisplay. Then
+ // acquire sleep token if screen is off.
+ if (!mScreenOnEarly && !mScreenOnFully && !mDisplayContent.isSleeping()) {
+ Slog.w(TAG, "Late acquire sleep token for " + mDisplayContent);
+ mService.mRoot.mDisplayOffTokenAcquirer.acquire(mDisplayContent.mDisplayId);
+ }
}
}
}
@@ -851,6 +859,7 @@
public void screenTurningOn(ScreenOnListener screenOnListener) {
WindowProcessController visibleDozeUiProcess = null;
synchronized (mLock) {
+ mService.mRoot.mDisplayOffTokenAcquirer.release(mDisplayContent.mDisplayId);
mScreenOnEarly = true;
mScreenOnFully = false;
mKeyguardDrawComplete = false;
@@ -875,8 +884,12 @@
onDisplaySwitchFinished();
}
- public void screenTurnedOff() {
+ /** It is called after {@link #screenTurningOn}. This runs on PowerManager's thread. */
+ public void screenTurnedOff(boolean acquireSleepToken) {
synchronized (mLock) {
+ if (acquireSleepToken) {
+ mService.mRoot.mDisplayOffTokenAcquirer.acquire(mDisplayContent.mDisplayId);
+ }
mScreenOnEarly = false;
mScreenOnFully = false;
mKeyguardDrawComplete = false;
@@ -1036,7 +1049,7 @@
/**
* Check if a window can be added to the system.
*
- * Currently enforces that two window types are singletons per display:
+ * Currently enforces that these window types are singletons per display:
* <ul>
* <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li>
* <li>{@link WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}</li>
@@ -1058,41 +1071,39 @@
ActivityTaskManagerService.enforceTaskPermission("DisplayPolicy");
}
+ final String systemUiPermission =
+ mService.isCallerVirtualDeviceOwner(mDisplayContent.getDisplayId(), callingUid)
+ // Allow virtual device owners to add system windows on their displays.
+ ? android.Manifest.permission.CREATE_VIRTUAL_DEVICE
+ : android.Manifest.permission.STATUS_BAR_SERVICE;
+
switch (attrs.type) {
case TYPE_STATUS_BAR:
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
"DisplayPolicy");
if (mStatusBar != null && mStatusBar.isAlive()) {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NOTIFICATION_SHADE:
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
"DisplayPolicy");
- if (mNotificationShade != null) {
- if (mNotificationShade.isAlive()) {
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
+ if (mNotificationShade != null && mNotificationShade.isAlive()) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NAVIGATION_BAR:
- mContext.enforcePermission(android.Manifest.permission.STATUS_BAR_SERVICE,
- callingPid, callingUid, "DisplayPolicy");
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
+ "DisplayPolicy");
if (mNavigationBar != null && mNavigationBar.isAlive()) {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NAVIGATION_BAR_PANEL:
- mContext.enforcePermission(android.Manifest.permission.STATUS_BAR_SERVICE,
- callingPid, callingUid, "DisplayPolicy");
- break;
case TYPE_STATUS_BAR_ADDITIONAL:
case TYPE_STATUS_BAR_SUB_PANEL:
case TYPE_VOICE_INTERACTION_STARTING:
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
"DisplayPolicy");
break;
case TYPE_STATUS_BAR_PANEL:
@@ -1102,8 +1113,7 @@
if (attrs.providedInsets != null) {
// Recents component is allowed to add inset types.
if (!mService.mAtmService.isCallerRecents(callingUid)) {
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
"DisplayPolicy");
}
}
@@ -2507,7 +2517,7 @@
if (getStatusBar() != null) {
final StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
- statusBar.setTopAppHidesStatusBar(topAppHidesStatusBar);
+ statusBar.setTopAppHidesStatusBar(getDisplayId(), topAppHidesStatusBar);
}
}
@@ -2534,9 +2544,9 @@
mService.mPolicy.isUserSetupComplete(),
isNavBarEmpty(disableFlags));
} else {
- // TODO (b/277290737): Move this to the client side, instead of using a proxy.
- callStatusBarSafely(statusBar -> statusBar.immersiveModeChanged(rootDisplayAreaId,
- isImmersiveMode));
+ // TODO(b/277290737): Move this to the client side, instead of using a proxy.
+ callStatusBarSafely(statusBar -> statusBar.immersiveModeChanged(getDisplayId(),
+ rootDisplayAreaId, isImmersiveMode));
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 5200e82..8c06cfe 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -81,7 +81,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
-import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -136,7 +135,6 @@
private final RotationLockHistory mRotationLockHistory = new RotationLockHistory();
private OrientationListener mOrientationListener;
- private StatusBarManagerInternal mStatusBarManagerInternal;
private SettingsObserver mSettingsObserver;
@NonNull
private final DeviceStateController mDeviceStateController;
@@ -1559,11 +1557,9 @@
/** Notify the StatusBar that system rotation suggestion has changed. */
private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
- if (mStatusBarManagerInternal == null) {
- mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
- }
- if (mStatusBarManagerInternal != null) {
- mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
+ final StatusBarManagerInternal bar = mDisplayPolicy.getStatusBarManagerInternal();
+ if (bar != null) {
+ bar.onProposedRotationChanged(mDisplayContent.getDisplayId(), rotation, isValid);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 27d9767..efc3843 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -408,9 +408,26 @@
private void recomputeConfigurationForCameraCompatIfNeeded(
@NonNull ActivityRecord activityRecord) {
- if (activityRecord.mAppCompatController.getAppCompatCameraOverrides()
- .shouldRecomputeConfigurationForCameraCompat()) {
+ if (shouldRecomputeConfigurationForCameraCompat(activityRecord)) {
activityRecord.recomputeConfiguration();
}
}
+
+ /**
+ * @return {@code true} if the configuration needs to be recomputed after a camera state update.
+ */
+ private boolean shouldRecomputeConfigurationForCameraCompat(
+ @NonNull ActivityRecord activityRecord) {
+ final AppCompatCameraOverrides overrides = activityRecord.mAppCompatController
+ .getAppCompatCameraOverrides();
+ return overrides.isOverrideOrientationOnlyForCameraEnabled()
+ || overrides.isCameraCompatSplitScreenAspectRatioAllowed()
+ || shouldOverrideMinAspectRatio(activityRecord);
+ }
+
+ private boolean shouldOverrideMinAspectRatio(@NonNull ActivityRecord activityRecord) {
+ return activityRecord.mAppCompatController.getAppCompatCameraOverrides()
+ .isOverrideMinAspectRatioForCameraEnabled()
+ && isCameraActive(activityRecord, /* mustBeFullscreen= */ true);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index f40f2617..e585efa8 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -69,6 +69,7 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
+ /** Stores the size override settings. If the width or height is zero, it means to clear. */
void setForcedSize(@NonNull DisplayContent displayContent, int width, int height) {
if (displayContent.isDefaultDisplay) {
final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 59435b8..b09d63f 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -175,7 +175,7 @@
*/
private CompletableFuture<Void> showInputSurface() {
if (mInputSurface == null) {
- mInputSurface = mService.makeSurfaceBuilder(mDisplayContent.getSession())
+ mInputSurface = mService.makeSurfaceBuilder()
.setContainerLayer()
.setName("Drag and Drop Input Consumer")
.setCallsite("DragState.showInputSurface")
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/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 4204670..a288cc7 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -77,8 +77,7 @@
mWindowHandle.scaleFactor = 1.0f;
mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
- mInputSurface = mService.makeSurfaceBuilder(
- mService.mRoot.getDisplayContent(displayId).getSession())
+ mInputSurface = mService.makeSurfaceBuilder()
.setContainerLayer()
.setName("Input Consumer " + name)
.setCallsite("InputConsumerImpl")
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 1e7de2b..232c3b6 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -281,7 +281,7 @@
+ " - Input overlay layer is not initialized.");
return null;
}
- return mService.makeSurfaceBuilder(dc.getSession())
+ return mService.makeSurfaceBuilder()
.setContainerLayer()
.setName(name)
.setCallsite("createSurfaceForGestureMonitor")
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/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 5d8a96c..0c489d6 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -87,7 +87,7 @@
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
private RootWindowContainer mRootWindowContainer;
- private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
+ private final ActivityTaskManagerService.SleepTokenAcquirer mSleepTokenAcquirer;
private boolean mWaitingForWakeTransition;
private Transition.ReadyCondition mWaitAodHide = null;
@@ -95,7 +95,7 @@
ActivityTaskSupervisor taskSupervisor) {
mService = service;
mTaskSupervisor = taskSupervisor;
- mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
+ mSleepTokenAcquirer = mService.new SleepTokenAcquirer(KEYGUARD_SLEEP_TOKEN_TAG);
}
void setWindowManager(WindowManagerService windowManager) {
@@ -658,10 +658,10 @@
private boolean mRequestDismissKeyguard;
private final ActivityTaskManagerService mService;
- private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
+ private final ActivityTaskManagerService.SleepTokenAcquirer mSleepTokenAcquirer;
KeyguardDisplayState(ActivityTaskManagerService service, int displayId,
- ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) {
+ ActivityTaskManagerService.SleepTokenAcquirer acquirer) {
mService = service;
mDisplayId = displayId;
mSleepTokenAcquirer = acquirer;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 0dadade..e65396e 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -659,7 +659,7 @@
StatusBarManagerInternal statusBarManager = LocalServices.getService(
StatusBarManagerInternal.class);
if (statusBarManager != null) {
- statusBarManager.showScreenPinningRequest(task.mTaskId);
+ statusBarManager.showScreenPinningRequest(task.mTaskId, task.mUserId);
}
return;
} else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 781023c..5d6d8bc 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -24,6 +24,7 @@
per-file Background*Start* = set noparent
per-file Background*Start* = file:/BAL_OWNERS
per-file Background*Start* = [email protected], [email protected]
+per-file BackgroundLaunchProcessController.java = file:/BAL_OWNERS
# File related to activity callers
per-file ActivityCallerState.java = file:/core/java/android/app/COMPONENT_CALLER_OWNERS
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index f8665c7..432089f 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -53,6 +53,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Helper class to run app animations in a remote process.
@@ -348,6 +349,10 @@
} finally {
mIsFinishing = false;
}
+ // Reset input for all activities when the remote animation is finished.
+ final Consumer<ActivityRecord> updateActivities =
+ activity -> activity.setDropInputForAnimation(false);
+ mDisplayContent.forAllActivities(updateActivities);
}
setRunningRemoteAnimation(false);
ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 866dcd5..8f5612c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -215,7 +215,7 @@
private static final String DISPLAY_OFF_SLEEP_TOKEN_TAG = "Display-off";
/** The token acquirer to put root tasks on the displays to sleep */
- final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer;
+ final ActivityTaskManagerService.SleepTokenAcquirer mDisplayOffTokenAcquirer;
/**
* The modes which affect which tasks are returned when calling
@@ -450,7 +450,7 @@
mService = service.mAtmService;
mTaskSupervisor = mService.mTaskSupervisor;
mTaskSupervisor.mRootWindowContainer = this;
- mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG);
+ mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirer(DISPLAY_OFF_SLEEP_TOKEN_TAG);
mDeviceStateController = new DeviceStateController(service.mContext, service.mGlobalLock);
mDisplayRotationCoordinator = new DisplayRotationCoordinator();
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 7c875c1..2ea2aeb6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -38,7 +38,6 @@
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
-import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -72,7 +71,6 @@
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.View;
import android.view.View.FocusDirection;
import android.view.WindowInsets;
@@ -108,7 +106,6 @@
@NonNull
final WindowProcessController mProcess;
private final String mStringName;
- SurfaceSession mSurfaceSession;
private final ArrayList<WindowState> mAddedWindows = new ArrayList<>();
/** Set of visible alert/app-overlay windows connected to this session. */
private final ArraySet<WindowState> mAlertWindows = new ArraySet<>();
@@ -719,12 +716,10 @@
mPackageName = mProcess.mInfo.packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
}
- if (mSurfaceSession == null) {
+ if (mProcess.mWindowSession == null) {
if (DEBUG) {
- Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
+ Slog.v(TAG_WM, "First window added to " + mProcess);
}
- mSurfaceSession = new SurfaceSession();
- ProtoLog.i(WM_SHOW_TRANSACTIONS, " NEW SURFACE SESSION %s", mSurfaceSession);
mService.mSessions.add(this);
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
@@ -821,18 +816,11 @@
}
mService.mSessions.remove(this);
- if (mSurfaceSession == null) {
+ if (mProcess.mWindowSession == null) {
return;
}
- ProtoLog.i(WM_SHOW_TRANSACTIONS, " KILL SURFACE SESSION %s", mSurfaceSession);
- try {
- mSurfaceSession.kill();
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception thrown when killing surface session " + mSurfaceSession
- + " in session " + this + ": " + e.toString());
- }
- mSurfaceSession = null;
+ mProcess.mWindowSession = null;
mAddedWindows.clear();
mAlertWindows.clear();
setHasOverlayUi(false);
@@ -857,7 +845,6 @@
pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
pw.print(" mAlertWindows="); pw.print(mAlertWindows);
pw.print(" mClientDead="); pw.print(mClientDead);
- pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
pw.print(prefix); pw.print("mPackageName="); pw.println(mPackageName);
if (isSatellitePointingUiPackage()) {
pw.print(prefix); pw.println("mIsSatellitePointingUiPackage=true");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 21be0fc..3bb273c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3192,13 +3192,6 @@
return "Task=" + mTaskId;
}
- WindowContainer<?> getDimmerParent() {
- if (!inMultiWindowMode() && isTranslucentForTransition()) {
- return getRootDisplayArea();
- }
- return this;
- }
-
@Deprecated
@Override
Dimmer getDimmer() {
@@ -3222,6 +3215,13 @@
return mDimmer;
}
+ boolean isSuitableForDimming() {
+ // If the window is in multi-window mode, we want to dim at the Task level to ensure the dim
+ // bounds match the area the app lives in.
+ // If translucent, we will move the dim to the display area
+ return inMultiWindowMode() || !isTranslucentAndVisible();
+ }
+
@Override
void prepareSurfaces() {
mDimmer.resetDimStates();
@@ -3507,7 +3507,7 @@
* {@link android.window.TaskFragmentOrganizer}
*/
TaskFragmentParentInfo getTaskFragmentParentInfo() {
- return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(),
+ return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(), mTaskId,
shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity(),
getDecorSurface());
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index e4a3176..92953e5 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.window.TaskFragmentOrganizer.KEY_RESTORE_TASK_FRAGMENTS_INFO;
+import static android.window.TaskFragmentOrganizer.KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO;
import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
@@ -46,6 +48,7 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
@@ -156,6 +159,13 @@
private final boolean mIsSystemOrganizer;
/**
+ * {@link RemoteAnimationDefinition} for embedded activities transition animation that is
+ * organized by this organizer.
+ */
+ @Nullable
+ private RemoteAnimationDefinition mRemoteAnimationDefinition;
+
+ /**
* Map from {@link TaskFragmentTransaction#getTransactionToken()} to the
* {@link Transition#getSyncId()} that has been deferred. {@link TransitionController} will
* wait until the organizer finished handling the {@link TaskFragmentTransaction}.
@@ -198,7 +208,13 @@
mOrganizerPid = pid;
mAppThread = getAppThread(pid, mOrganizerUid);
for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
- mOrganizedTaskFragments.get(i).onTaskFragmentOrganizerRestarted(organizer);
+ final TaskFragment taskFragment = mOrganizedTaskFragments.get(i);
+ if (taskFragment.isAttached()
+ && taskFragment.getTopNonFinishingActivity() != null) {
+ taskFragment.onTaskFragmentOrganizerRestarted(organizer);
+ } else {
+ mOrganizedTaskFragments.remove(taskFragment);
+ }
}
try {
mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
@@ -567,8 +583,29 @@
}
mCachedTaskFragmentOrganizerStates.remove(cachedState);
- outSavedState.putAll(cachedState.mSavedState);
cachedState.restore(organizer, pid);
+ outSavedState.putAll(cachedState.mSavedState);
+
+ // Collect the organized TfInfo and TfParentInfo in the system.
+ final ArrayList<TaskFragmentInfo> infos = new ArrayList<>();
+ final ArrayMap<Integer, Task> tasks = new ArrayMap<>();
+ final int fragmentCount = cachedState.mOrganizedTaskFragments.size();
+ for (int j = 0; j < fragmentCount; j++) {
+ final TaskFragment tf = cachedState.mOrganizedTaskFragments.get(j);
+ infos.add(tf.getTaskFragmentInfo());
+ if (!tasks.containsKey(tf.getTask().mTaskId)) {
+ tasks.put(tf.getTask().mTaskId, tf.getTask());
+ }
+ }
+ outSavedState.putParcelableArrayList(KEY_RESTORE_TASK_FRAGMENTS_INFO, infos);
+
+ final ArrayList<TaskFragmentParentInfo> parentInfos = new ArrayList<>();
+ for (int j = tasks.size() - 1; j >= 0; j--) {
+ parentInfos.add(tasks.valueAt(j).getTaskFragmentParentInfo());
+ }
+ outSavedState.putParcelableArrayList(KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO,
+ parentInfos);
+
mTaskFragmentOrganizerState.put(organizer.asBinder(), cachedState);
mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
return true;
@@ -592,6 +629,50 @@
}
@Override
+ public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull RemoteAnimationDefinition definition) {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Register remote animations for organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ final TaskFragmentOrganizerState organizerState =
+ mTaskFragmentOrganizerState.get(organizer.asBinder());
+ if (organizerState == null) {
+ throw new IllegalStateException("The organizer hasn't been registered.");
+ }
+ if (organizerState.mRemoteAnimationDefinition != null) {
+ throw new IllegalStateException(
+ "The organizer has already registered remote animations="
+ + organizerState.mRemoteAnimationDefinition);
+ }
+
+ definition.setCallingPidUid(pid, uid);
+ organizerState.mRemoteAnimationDefinition = definition;
+ }
+ }
+
+ @Override
+ public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer) {
+ final int pid = Binder.getCallingPid();
+ final long uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Unregister remote animations for organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ final TaskFragmentOrganizerState organizerState =
+ mTaskFragmentOrganizerState.get(organizer.asBinder());
+ if (organizerState == null) {
+ Slog.e(TAG, "The organizer hasn't been registered.");
+ return;
+ }
+
+ organizerState.mRemoteAnimationDefinition = null;
+ }
+ }
+
+ @Override
public void setSavedState(@NonNull ITaskFragmentOrganizer organizer, @Nullable Bundle state) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -649,6 +730,25 @@
}
}
+ /**
+ * Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
+ * {@code null} if it doesn't.
+ */
+ @Nullable
+ public RemoteAnimationDefinition getRemoteAnimationDefinition(
+ @NonNull ITaskFragmentOrganizer organizer) {
+ synchronized (mGlobalLock) {
+ final TaskFragmentOrganizerState organizerState =
+ mTaskFragmentOrganizerState.get(organizer.asBinder());
+ if (organizerState == null) {
+ Slog.e(TAG, "TaskFragmentOrganizer has been unregistered or died when trying"
+ + " to play animation on its organized windows.");
+ return null;
+ }
+ return organizerState.mRemoteAnimationDefinition;
+ }
+ }
+
int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
return state.mOrganizerUid;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7f6dc84..366cf4d 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -73,6 +73,7 @@
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
+import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -223,6 +224,13 @@
private final ArrayList<Task> mOnTopTasksAtReady = new ArrayList<>();
/**
+ * Tracks the top display like top tasks so we can trigger a MOVED_TO_TOP transition even when
+ * a display gets moved to front but there's no change in per-display focused tasks.
+ */
+ private DisplayContent mOnTopDisplayStart = null;
+ private DisplayContent mOnTopDisplayAtReady = null;
+
+ /**
* Set of participating windowtokens (activity/wallpaper) which are visible at the end of
* the transition animation.
*/
@@ -484,12 +492,8 @@
boolean canApplyDim(@NonNull Task task) {
if (mTransientLaunches == null) return true;
if (Flags.useTasksDimOnly()) {
- WindowContainer<?> dimmerParent = task.getDimmerParent();
- if (dimmerParent == null) {
- return false;
- }
- // Always allow to dim if the host only affects its task.
- if (dimmerParent.asTask() == task) {
+ if (task.isSuitableForDimming()) {
+ // Always allow to dim if the dimming occurs at task level (dim parented to task)
return true;
}
} else {
@@ -772,6 +776,10 @@
if (dc == null || mTargetDisplays.contains(dc)) return;
mTargetDisplays.add(dc);
addOnTopTasks(dc, mOnTopTasksStart);
+ if (mOnTopDisplayStart == null) {
+ mOnTopDisplayStart =
+ mController.mAtm.mRootWindowContainer.getTopFocusedDisplayContent();
+ }
// Handle the case {transition.start(); applyTransaction(wct);} that the animating state
// is set before collecting participants.
if (mController.isAnimating()) {
@@ -953,13 +961,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;
@@ -998,6 +1003,8 @@
for (int i = 0; i < mTargetDisplays.size(); ++i) {
addOnTopTasks(mTargetDisplays.get(i), mOnTopTasksAtReady);
}
+ mOnTopDisplayAtReady =
+ mController.mAtm.mRootWindowContainer.getTopFocusedDisplayContent();
mController.onTransitionPopulated(this);
}
}
@@ -2082,6 +2089,10 @@
return true;
}
}
+ if (enableDisplayFocusInShellTransitions() && mOnTopDisplayStart
+ != mController.mAtm.mRootWindowContainer.getTopFocusedDisplayContent()) {
+ return true;
+ }
return false;
}
@@ -2113,6 +2124,8 @@
includesOrderChange = true;
break;
}
+ includesOrderChange |= enableDisplayFocusInShellTransitions()
+ && mOnTopDisplayStart != mOnTopDisplayAtReady;
if (!includesOrderChange && !reportCurrent) {
// This transition doesn't include an order change, so if it isn't required to report
// the current focus (eg. it's the last of a cluster of transitions), then don't
@@ -2123,6 +2136,8 @@
// latest state and compare with the last reported state (or our start state if no
// reported state exists).
ArrayList<Task> onTopTasksEnd = new ArrayList<>();
+ final DisplayContent onTopDisplayEnd =
+ mController.mAtm.mRootWindowContainer.getTopFocusedDisplayContent();
for (int d = 0; d < mTargetDisplays.size(); ++d) {
addOnTopTasks(mTargetDisplays.get(d), onTopTasksEnd);
final int displayId = mTargetDisplays.get(d).mDisplayId;
@@ -2130,11 +2145,15 @@
for (int i = onTopTasksEnd.size() - 1; i >= 0; --i) {
final Task task = onTopTasksEnd.get(i);
if (task.getDisplayId() != displayId) continue;
- // If it didn't change since last report, don't report
- if (reportedOnTop == null) {
- if (mOnTopTasksStart.contains(task)) continue;
- } else if (reportedOnTop.contains(task)) {
- continue;
+ if (!enableDisplayFocusInShellTransitions()
+ || mOnTopDisplayStart == onTopDisplayEnd
+ || displayId != onTopDisplayEnd.mDisplayId) {
+ // If it didn't change since last report, don't report
+ if (reportedOnTop == null) {
+ if (mOnTopTasksStart.contains(task)) continue;
+ } else if (reportedOnTop.contains(task)) {
+ continue;
+ }
}
// Need to report it.
mParticipants.add(task);
@@ -2336,10 +2355,7 @@
// Place the nav bar on top of anything else in the top activity.
t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
}
- final StatusBarManagerInternal bar = dc.getDisplayPolicy().getStatusBarManagerInternal();
- if (bar != null) {
- bar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, false);
- }
+ sendLumaSamplingEnabledToStatusBarInternal(dc, false);
}
/** @see RecentsAnimationController#restoreNavigationBarFromApp */
@@ -2357,10 +2373,7 @@
final DisplayContent dc =
mController.mAtm.mRootWindowContainer.getDisplayContent(recentsDisplayId);
- final StatusBarManagerInternal bar = dc.getDisplayPolicy().getStatusBarManagerInternal();
- if (bar != null) {
- bar.setNavigationBarLumaSamplingEnabled(recentsDisplayId, true);
- }
+ sendLumaSamplingEnabledToStatusBarInternal(dc, true);
final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
if (navWindow == null) return;
navWindow.setSurfaceTranslationY(0);
@@ -2394,6 +2407,14 @@
dc.mWmService.scheduleAnimationLocked();
}
+ private void sendLumaSamplingEnabledToStatusBarInternal(@NonNull DisplayContent dc,
+ boolean enabled) {
+ final StatusBarManagerInternal bar = dc.getDisplayPolicy().getStatusBarManagerInternal();
+ if (bar != null) {
+ bar.setNavigationBarLumaSamplingEnabled(dc.getDisplayId(), enabled);
+ }
+ }
+
private void reportStartReasonsToLogger() {
// Record transition start in metrics logger. We just assume everything is "DRAWN"
// at this point since splash-screen is a presentation (shell) detail.
@@ -2822,7 +2843,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 +3014,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 +3024,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 +3036,6 @@
params.getOpenAnimationResId(), params.getChangeAnimationResId(),
params.getCloseAnimationResId(), 0 /* backgroundColor */,
false /* overrideTaskTransition */);
- animOptions.setUserId(taskFragment.getTask().mUserId);
}
}
if (animOptions != null) {
@@ -3081,9 +3099,6 @@
animOptions);
animOptions = addCustomActivityTransition(customAnimActivity, false /* open */,
animOptions);
- if (animOptions != null) {
- animOptions.setUserId(customAnimActivity.mUserId);
- }
}
// Layout parameters
@@ -3097,12 +3112,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 +3141,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 +3269,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 +3316,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 +3925,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/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 5f3c558..030e848 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -51,7 +51,7 @@
void requireOverlaySurfaceControl() {
if (mSurfaceControl == null) {
- final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
+ final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder()
.setContainerLayer()
.setHidden(true)
.setCallsite("TrustedOverlayHost.requireOverlaySurfaceControl")
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/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6995027..1eeb3ec 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -100,7 +100,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.WindowManager;
import android.view.WindowManager.TransitionOldType;
import android.view.animation.Animation;
@@ -706,7 +705,7 @@
mLastSurfacePosition.set(0, 0);
mLastDeltaRotation = Surface.ROTATION_0;
- final Builder b = mWmService.makeSurfaceBuilder(null)
+ final Builder b = mWmService.makeSurfaceBuilder()
.setContainerLayer()
.setName(getName());
@@ -1731,13 +1730,13 @@
* last time {@link #getOrientation(int) was called.
*/
@Nullable
- WindowContainer getLastOrientationSource() {
- final WindowContainer source = mLastOrientationSource;
- if (source != null && source != this) {
- final WindowContainer nextSource = source.getLastOrientationSource();
- if (nextSource != null) {
- return nextSource;
- }
+ final WindowContainer<?> getLastOrientationSource() {
+ if (mLastOrientationSource == null) {
+ return null;
+ }
+ WindowContainer<?> source = this;
+ while (source != source.mLastOrientationSource && source.mLastOrientationSource != null) {
+ source = source.mLastOrientationSource;
}
return source;
}
@@ -2662,13 +2661,6 @@
return true;
}
- SurfaceSession getSession() {
- if (getParent() != null) {
- return getParent().getSession();
- }
- return null;
- }
-
void assignLayer(Transaction t, int layer) {
// Don't assign layers while a transition animation is playing
// TODO(b/173528115): establish robust best-practices around z-order fighting.
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index 1931be4..e0f24d8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -34,6 +34,10 @@
*/
final class WindowManagerConstants {
+ /** The orientation of activity will be always "unspecified" except for game apps. */
+ private static final String KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST =
+ "ignore_activity_orientation_request";
+
/**
* The minimum duration between gesture exclusion logging for a given window in
* milliseconds.
@@ -58,6 +62,9 @@
/** @see AndroidDeviceConfig#KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE */
boolean mSystemGestureExcludedByPreQStickyImmersive;
+ /** @see #KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST */
+ boolean mIgnoreActivityOrientationRequest;
+
private final WindowManagerGlobalLock mGlobalLock;
private final Runnable mUpdateSystemGestureExclusionCallback;
private final DeviceConfigInterface mDeviceConfig;
@@ -89,6 +96,7 @@
updateSystemGestureExclusionLogDebounceMillis();
updateSystemGestureExclusionLimitDp();
updateSystemGestureExcludedByPreQStickyImmersive();
+ updateIgnoreActivityOrientationRequest();
}
private void onAndroidPropertiesChanged(DeviceConfig.Properties properties) {
@@ -127,6 +135,9 @@
case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS:
updateSystemGestureExclusionLogDebounceMillis();
break;
+ case KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST:
+ updateIgnoreActivityOrientationRequest();
+ break;
default:
break;
}
@@ -152,6 +163,12 @@
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
}
+ private void updateIgnoreActivityOrientationRequest() {
+ mIgnoreActivityOrientationRequest = mDeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST, false);
+ }
+
void dump(PrintWriter pw) {
pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):");
@@ -161,6 +178,8 @@
pw.print("="); pw.println(mSystemGestureExclusionLimitDp);
pw.print(" "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
+ pw.print(" "); pw.print(KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST);
+ pw.print("="); pw.println(mIgnoreActivityOrientationRequest);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 979b3a5..459a509 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -90,6 +90,7 @@
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
@@ -158,6 +159,7 @@
import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID;
import static com.android.window.flags.Flags.multiCrop;
import static com.android.window.flags.Flags.setScPropertiesInClient;
+import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import android.Manifest;
import android.Manifest.permission;
@@ -288,7 +290,6 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.View;
import android.view.View.FocusDirection;
import android.view.ViewDebug;
@@ -386,7 +387,6 @@
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
import java.util.function.Supplier;
/** {@hide} */
@@ -1111,7 +1111,7 @@
static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
new WindowManagerThreadPriorityBooster();
- Function<SurfaceSession, SurfaceControl.Builder> mSurfaceControlFactory;
+ Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
Supplier<SurfaceControl.Transaction> mTransactionFactory;
private final SurfaceControl.Transaction mTransaction;
@@ -1202,7 +1202,7 @@
final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
DisplayWindowSettingsProvider displayWindowSettingsProvider,
Supplier<SurfaceControl.Transaction> transactionFactory,
- Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
+ Supplier<SurfaceControl.Builder> surfaceControlFactory) {
final WindowManagerService[] wms = new WindowManagerService[1];
DisplayThread.getHandler().runWithScissors(() ->
wms[0] = new WindowManagerService(context, im, showBootMsgs, policy, atm,
@@ -1231,7 +1231,7 @@
boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
DisplayWindowSettingsProvider displayWindowSettingsProvider,
Supplier<SurfaceControl.Transaction> transactionFactory,
- Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
+ Supplier<SurfaceControl.Builder> surfaceControlFactory) {
installLock(this, INDEX_WINDOW);
mGlobalLock = atm.getGlobalLock();
mAtmService = atm;
@@ -1526,7 +1526,7 @@
final boolean isRoundedCornerOverlay = (attrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
- appOp);
+ appOp, displayId);
if (res != ADD_OKAY) {
return res;
}
@@ -1547,7 +1547,23 @@
return WindowManagerGlobal.ADD_APP_EXITING;
}
- final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
+ if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
+ parentWindow = windowForClientLocked(null, attrs.token, false);
+ if (parentWindow == null) {
+ ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
+ + "%s. Aborting.", attrs.token);
+ return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
+ }
+ if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
+ && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
+ ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
+ + "%s. Aborting.", attrs.token);
+ return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
+ }
+ }
+ final DisplayContent displayContent = parentWindow != null
+ ? parentWindow.mDisplayContent
+ : getDisplayContentOrCreate(displayId, attrs.token);
if (displayContent == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
@@ -1567,21 +1583,6 @@
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
- if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
- parentWindow = windowForClientLocked(null, attrs.token, false);
- if (parentWindow == null) {
- ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
- }
- if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
- && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
- ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
- }
- }
-
if (type == TYPE_PRESENTATION || type == TYPE_PRIVATE_PRESENTATION) {
mDisplayManagerInternal.onPresentation(displayContent.getDisplay().getDisplayId(),
/*isShown=*/ true);
@@ -3240,9 +3241,28 @@
return;
}
+ Transition transition = null;
+ boolean transitionNewlyCreated = false;
+ if (enableDisplayFocusInShellTransitions()) {
+ transition = mAtmService.getTransitionController().requestTransitionIfNeeded(
+ TRANSIT_TO_FRONT, 0 /* flags */, null /* trigger */,
+ displayContent);
+ if (transition != null) {
+ transitionNewlyCreated = true;
+ } else {
+ transition =
+ mAtmService.getTransitionController().getCollectingTransition();
+ }
+ if (transition != null) {
+ transition.recordTaskOrder(displayContent);
+ }
+ }
// Nothing prevented us from moving the display to the top. Let's do it!
displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
displayContent, true /* includingParents */);
+ if (transitionNewlyCreated) {
+ transition.setReady(displayContent, true /* ready */);
+ }
}
}
}
@@ -6025,7 +6045,7 @@
displayContent.setForcedSize(displayContent.mInitialDisplayWidth,
displayContent.mInitialDisplayHeight,
displayContent.mInitialPhysicalXDpi,
- displayContent.mInitialPhysicalXDpi);
+ displayContent.mInitialPhysicalYDpi);
}
}
} finally {
@@ -7920,7 +7940,7 @@
}
boolean allWindowsDrawn = false;
synchronized (mGlobalLock) {
- if (displayId == DEFAULT_DISPLAY
+ if (displayId == INVALID_DISPLAY
&& mRoot.getDefaultDisplay().mDisplayUpdater.waitForTransition(message)) {
// Use the ready-to-play of transition as the signal.
return;
@@ -8521,7 +8541,7 @@
return null;
}
// TODO(b/210039666): Use a method like add/removeDisplayOverlay if available.
- return makeSurfaceBuilder(dc.getSession())
+ return makeSurfaceBuilder()
.setContainerLayer()
.setName("IME Handwriting Surface")
.setCallsite("getHandwritingSurfaceForDisplay")
@@ -8829,8 +8849,8 @@
}
}
- SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
- return mSurfaceControlFactory.apply(s);
+ SurfaceControl.Builder makeSurfaceBuilder() {
+ return mSurfaceControlFactory.get();
}
/**
@@ -10123,6 +10143,23 @@
}
}
+ /**
+ * Returns whether the given UID is the owner of a virtual device, which the given display
+ * belongs to.
+ */
+ @Override
+ public boolean isCallerVirtualDeviceOwner(int displayId, int callingUid) {
+ if (!android.companion.virtualdevice.flags.Flags.statusBarAndInsets()) {
+ return false;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mAtmService.mTaskSupervisor.isDeviceOwnerUid(displayId, callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@RequiresPermission(ACCESS_SURFACE_FLINGER)
@Override
public boolean replaceContentOnDisplay(int displayId, SurfaceControl sc) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index d96ebc6..87ce866 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -89,6 +89,7 @@
import com.android.server.Watchdog;
import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
import com.android.server.wm.ActivityTaskManagerService.HotPath;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
import java.io.IOException;
import java.io.PrintWriter;
@@ -357,7 +358,6 @@
mUseFifoUiScheduling = com.android.window.flags.Flags.fifoPriorityForMajorUiProcesses()
&& (isSysUiPackage || mAtm.isCallerRecents(uid));
- onConfigurationChanged(atm.getGlobalConfiguration());
mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName);
}
@@ -695,20 +695,13 @@
public boolean areBackgroundFgsStartsAllowed() {
return areBackgroundActivityStartsAllowed(
mAtm.getBalAppSwitchesState(),
- true /* isCheckingForFgsStart */).allows();
+ BackgroundLaunchProcessController.CHECK_FOR_FGS_START).allows();
}
BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
- int appSwitchState) {
- return areBackgroundActivityStartsAllowed(
- appSwitchState,
- false /* isCheckingForFgsStart */);
- }
-
- private BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
- int appSwitchState, boolean isCheckingForFgsStart) {
+ int appSwitchState, BalCheckConfiguration checkConfiguration) {
return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid,
- mInfo.packageName, appSwitchState, isCheckingForFgsStart,
+ mInfo.packageName, appSwitchState, checkConfiguration,
hasActivityInVisibleTask(), mInstrumentingWithBackgroundActivityStartPrivileges,
mAtm.getLastStopAppSwitchesTime(),
mLastActivityLaunchTime, mLastActivityFinishTime);
@@ -1932,7 +1925,12 @@
// showing.
// If the configuration has been overridden by previous activity, empty it.
mIsActivityConfigOverrideAllowed = false;
- unregisterActivityConfigurationListener();
+ // The call to `onServiceStarted` is not guarded with WM lock.
+ mAtm.mH.post(() -> {
+ synchronized (mAtm.mGlobalLock) {
+ unregisterActivityConfigurationListener();
+ }
+ });
break;
default:
break;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7c05c29..021be57 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -233,7 +233,6 @@
import android.view.Surface;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewTreeObserver;
@@ -5162,15 +5161,6 @@
}
@Override
- SurfaceSession getSession() {
- if (mSession.mSurfaceSession != null) {
- return mSession.mSurfaceSession;
- } else {
- return getParent().getSession();
- }
- }
-
- @Override
boolean needsZBoost() {
final InsetsControlTarget target = getDisplayContent().getImeTarget(IME_TARGET_LAYERING);
if (mIsImWindow && target != null) {
@@ -5206,14 +5196,8 @@
Dimmer dimmer;
WindowContainer<?> geometryParent = task;
if (Flags.useTasksDimOnly()) {
- if (task != null) {
- geometryParent = task.getDimmerParent();
- dimmer = task.mDimmer;
- } else {
- RootDisplayArea displayArea = getRootDisplayArea();
- geometryParent = displayArea;
- dimmer = displayArea != null ? displayArea.getDimmer() : null;
- }
+ geometryParent = getDimParent();
+ dimmer = getDimController();
if (dimmer == null) {
ProtoLog.e(WM_DEBUG_DIMMER, "WindowState %s does not have task or"
+ " display area for dimming", this);
@@ -5226,11 +5210,30 @@
if (isVisibleNow()) {
dimmer.adjustAppearance(this, dimAmount, blurRadius);
}
- dimmer.adjustPosition(geometryParent,
- this /* relativeParent */, -1 /* relativeLayer */);
+ dimmer.adjustPosition(geometryParent, this /* relativeParent */);
}
}
+ private Dimmer getDimController() {
+ Task task = getTask();
+ if (task != null) {
+ return task.mDimmer;
+ }
+ RootDisplayArea displayArea = getRootDisplayArea();
+ if (displayArea != null) {
+ return displayArea.getDimmer();
+ }
+ return null;
+ }
+
+ private WindowContainer<?> getDimParent() {
+ Task task = getTask();
+ if (task != null && task.isSuitableForDimming()) {
+ return task;
+ }
+ return getRootDisplayArea();
+ }
+
private boolean shouldDrawBlurBehind() {
return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0
&& mWmService.mBlurController.getBlurEnabled();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b40cf56..82fa9d4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -321,8 +321,7 @@
}
if (DEBUG_VISIBILITY) {
- Slog.v(TAG, "Creating surface in session "
- + mSession.mSurfaceSession + " window " + this
+ Slog.v(TAG, "Creating surface " + this
+ " format=" + attrs.format + " flags=" + flags);
}
@@ -358,9 +357,8 @@
w.mInputWindowHandle.forceChange();
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
- " CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s",
- mSurfaceControl, mSession.mSurfaceSession, mSession.mPid, attrs.format,
- flags, this);
+ " CREATE SURFACE %s: pid=%d format=%d flags=0x%x / %s",
+ mSurfaceControl, mSession.mPid, attrs.format, flags, this);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "OutOfResourcesException creating surface");
mService.mRoot.reclaimSomeSurfaceMemory(this, "create", true);
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 5c5ac28..39c0c3e 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -54,6 +54,9 @@
jmethodID setCompositionSizeMax;
jmethodID setQFactor;
jmethodID setFrequencyProfile;
+ jmethodID setMaxEnvelopeEffectSize;
+ jmethodID setMinEnvelopeEffectControlPointDurationMillis;
+ jmethodID setMaxEnvelopeEffectControlPointDurationMillis;
} sVibratorInfoBuilderClassInfo;
static struct {
jfieldID id;
@@ -484,6 +487,25 @@
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setQFactor,
static_cast<jfloat>(info.qFactor.value()));
}
+ if (info.maxEnvelopeEffectSize.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setMaxEnvelopeEffectSize,
+ static_cast<jint>(info.maxEnvelopeEffectSize.value()));
+ }
+ if (info.minEnvelopeEffectControlPointDuration.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo
+ .setMinEnvelopeEffectControlPointDurationMillis,
+ static_cast<jint>(
+ info.minEnvelopeEffectControlPointDuration.value().count()));
+ }
+ if (info.maxEnvelopeEffectControlPointDuration.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo
+ .setMaxEnvelopeEffectControlPointDurationMillis,
+ static_cast<jint>(
+ info.maxEnvelopeEffectControlPointDuration.value().count()));
+ }
jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
@@ -580,6 +602,17 @@
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyProfile",
"(Landroid/os/VibratorInfo$FrequencyProfile;)"
"Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setMaxEnvelopeEffectSize =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setMaxEnvelopeEffectSize",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setMinEnvelopeEffectControlPointDurationMillis =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass,
+ "setMinEnvelopeEffectControlPointDurationMillis",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setMaxEnvelopeEffectControlPointDurationMillis =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass,
+ "setMaxEnvelopeEffectControlPointDurationMillis",
+ "(I)Landroid/os/VibratorInfo$Builder;");
return jniRegisterNativeMethods(env,
"com/android/server/vibrator/VibratorController$NativeWrapper",
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 4231149..0eafb59 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -42,7 +42,7 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
- <xs:element type="thermalThrottling" name="thermalThrottling">
+ <xs:element type="thermalThrottling" name="thermalThrottling" minOccurs="0" maxOccurs="1">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
@@ -464,7 +464,15 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
- <xs:element name="pollingWindowMillis" type="xs:nonNegativeInteger">
+ <xs:element name="customAnimationRateSec" type="nonNegativeDecimal" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element name="pollingWindowMaxMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element name="pollingWindowMinMillis" type="xs:nonNegativeInteger">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index cec2787..355b0ab 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -345,10 +345,14 @@
public class PowerThrottlingConfig {
ctor public PowerThrottlingConfig();
method @NonNull public final java.math.BigDecimal getBrightnessLowestCapAllowed();
- method @NonNull public final java.math.BigInteger getPollingWindowMillis();
+ method @NonNull public final java.math.BigDecimal getCustomAnimationRateSec();
+ method @NonNull public final java.math.BigInteger getPollingWindowMaxMillis();
+ method @NonNull public final java.math.BigInteger getPollingWindowMinMillis();
method public final java.util.List<com.android.server.display.config.PowerThrottlingMap> getPowerThrottlingMap();
method public final void setBrightnessLowestCapAllowed(@NonNull java.math.BigDecimal);
- method public final void setPollingWindowMillis(@NonNull java.math.BigInteger);
+ method public final void setCustomAnimationRateSec(@NonNull java.math.BigDecimal);
+ method public final void setPollingWindowMaxMillis(@NonNull java.math.BigInteger);
+ method public final void setPollingWindowMinMillis(@NonNull java.math.BigInteger);
}
public class PowerThrottlingMap {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 5eec012..b982098 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -1325,11 +1325,6 @@
pw.print("encryptionRequested=");
pw.println(encryptionRequested);
- if (!Flags.policyEngineMigrationV2Enabled()) {
- pw.print("mUsbDataSignaling=");
- pw.println(mUsbDataSignalingEnabled);
- }
-
pw.print("disableCallerId=");
pw.println(disableCallerId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index a08af72..4beb6a8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -230,11 +230,9 @@
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
- policyDefinition, userId)) {
- return;
- }
+ if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
+ policyDefinition, userId)) {
+ return;
}
if (policyDefinition.isNonCoexistablePolicy()) {
@@ -354,9 +352,7 @@
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
- }
+ decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
if (policyDefinition.isNonCoexistablePolicy()) {
setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
@@ -500,11 +496,9 @@
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
- policyDefinition, UserHandle.USER_ALL)) {
- return;
- }
+ if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
+ policyDefinition, UserHandle.USER_ALL)) {
+ return;
}
// TODO(b/270999567): Move error handling for DISALLOW_CELLULAR_2G into the code
// that honors the restriction once there's an API available
@@ -571,9 +565,7 @@
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- decreasePolicySizeForAdmin(policyState, enforcingAdmin);
- }
+ decreasePolicySizeForAdmin(policyState, enforcingAdmin);
boolean policyChanged = policyState.removePolicy(enforcingAdmin);
@@ -1739,25 +1731,23 @@
pw.println();
}
pw.decreaseIndent();
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- pw.println();
+ pw.println();
- pw.println("Default admin policy size limit: " + DEFAULT_POLICY_SIZE_LIMIT);
- pw.println("Current admin policy size limit: " + mPolicySizeLimit);
- pw.println("Admin Policies size: ");
- for (int i = 0; i < mAdminPolicySize.size(); i++) {
- int userId = mAdminPolicySize.keyAt(i);
- pw.printf("User %d:\n", userId);
- pw.increaseIndent();
- for (EnforcingAdmin admin : mAdminPolicySize.get(userId).keySet()) {
- pw.printf("Admin : " + admin + " : " + mAdminPolicySize.get(userId).get(
- admin));
- pw.println();
- }
- pw.decreaseIndent();
+ pw.println("Default admin policy size limit: " + DEFAULT_POLICY_SIZE_LIMIT);
+ pw.println("Current admin policy size limit: " + mPolicySizeLimit);
+ pw.println("Admin Policies size: ");
+ for (int i = 0; i < mAdminPolicySize.size(); i++) {
+ int userId = mAdminPolicySize.keyAt(i);
+ pw.printf("User %d:\n", userId);
+ pw.increaseIndent();
+ for (EnforcingAdmin admin : mAdminPolicySize.get(userId).keySet()) {
+ pw.printf("Admin : " + admin + " : " + mAdminPolicySize.get(userId).get(
+ admin));
+ pw.println();
}
pw.decreaseIndent();
}
+ pw.decreaseIndent();
}
}
@@ -2018,23 +2008,21 @@
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- if (mAdminPolicySize != null) {
- for (int i = 0; i < mAdminPolicySize.size(); i++) {
- int userId = mAdminPolicySize.keyAt(i);
- for (EnforcingAdmin admin : mAdminPolicySize.get(
- userId).keySet()) {
- serializer.startTag(/* namespace= */ null,
- TAG_ENFORCING_ADMIN_AND_SIZE);
- serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
- admin.saveToXml(serializer);
- serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
- serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
- serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
- mAdminPolicySize.get(userId).get(admin));
- serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
- serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
- }
+ if (mAdminPolicySize != null) {
+ for (int i = 0; i < mAdminPolicySize.size(); i++) {
+ int userId = mAdminPolicySize.keyAt(i);
+ for (EnforcingAdmin admin : mAdminPolicySize.get(
+ userId).keySet()) {
+ serializer.startTag(/* namespace= */ null,
+ TAG_ENFORCING_ADMIN_AND_SIZE);
+ serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+ admin.saveToXml(serializer);
+ serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+ serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+ serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
+ mAdminPolicySize.get(userId).get(admin));
+ serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+ serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
}
}
}
@@ -2042,9 +2030,6 @@
private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- return;
- }
serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
serializer.attributeInt(
/* namespace= */ null, ATTR_POLICY_SUM_SIZE, mPolicySizeLimit);
@@ -2192,9 +2177,6 @@
private void readMaxPolicySizeInner(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- return;
- }
mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 886ae7a..b6e45fc8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1328,9 +1328,7 @@
Bundle prevRestrictions) {
resetCrossProfileIntentFiltersIfNeeded(userId, newRestrictions, prevRestrictions);
resetUserVpnIfNeeded(userId, newRestrictions, prevRestrictions);
- if (Flags.deletePrivateSpaceUnderRestriction()) {
- removePrivateSpaceIfRestrictionIsSet(userId, newRestrictions, prevRestrictions);
- }
+ removePrivateSpaceIfRestrictionIsSet(userId, newRestrictions, prevRestrictions);
}
private void resetUserVpnIfNeeded(
@@ -3695,9 +3693,6 @@
}
revertTransferOwnershipIfNecessaryLocked();
- if (!Flags.policyEngineMigrationV2Enabled()) {
- updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
- }
}
// Check whether work apps were paused via suspension and unsuspend if necessary.
@@ -7156,9 +7151,7 @@
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwnerAsUser(caller.getUserId());
- boolean isDoUser = Flags.headlessSingleUserFixes()
- ? caller.getUserId() == getDeviceOwnerUserId()
- : caller.getUserHandle().isSystem();
+ boolean isDoUser = caller.getUserId() == getDeviceOwnerUserId();
if (aliasChooser == null && isDoUser) {
synchronized (getLockObject()) {
final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
@@ -8168,7 +8161,7 @@
// First check whether the admin is allowed to wipe the device/user/profile.
final String restriction;
boolean shouldFactoryReset = userId == UserHandle.USER_SYSTEM;
- if (Flags.headlessSingleUserFixes() && getHeadlessDeviceOwnerModeForDeviceOwner()
+ if (getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
shouldFactoryReset = userId == getMainUserId();
}
@@ -8192,8 +8185,7 @@
adminPackage,
userId)) {
// Legacy mode
- wipeDevice = Flags.headlessSingleUserFixes()
- && getHeadlessDeviceOwnerModeForDeviceOwner()
+ wipeDevice = getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER ? isMainUser : isSystemUser;
} else {
// Explicit behaviour
@@ -9377,8 +9369,7 @@
void sendDeviceOwnerOrProfileOwnerCommand(String action, Bundle extras, int userId) {
if (userId == UserHandle.USER_ALL) {
- if (Flags.headlessDeviceOwnerDelegateSecurityLoggingBugFix()
- && getHeadlessDeviceOwnerModeForDeviceOwner()
+ if (getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
userId = mOwners.getDeviceOwnerUserId();
} else {
@@ -11864,7 +11855,7 @@
}
setBackwardsCompatibleAppRestrictions(
caller, packageName, restrictions, caller.getUserHandle());
- } else if (Flags.dmrhSetAppRestrictions()) {
+ } else {
final boolean isRoleHolder;
if (who != null) {
// DO or PO
@@ -11911,15 +11902,6 @@
caller.getUserHandle());
});
}
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && isCallerDelegate(caller,
- DELEGATION_APP_RESTRICTIONS)));
- mInjector.binderWithCleanCallingIdentity(() -> {
- mUserManager.setApplicationRestrictions(packageName, restrictions,
- caller.getUserHandle());
- });
}
DevicePolicyEventLogger
@@ -12452,12 +12434,6 @@
}
if (packageList != null) {
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- for (String pkg : packageList) {
- PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
- }
- }
-
List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() ->
InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId));
if (enabledImes != null) {
@@ -13256,7 +13232,7 @@
return Bundle.EMPTY;
}
return policies.get(enforcingAdmin).getValue();
- } else if (Flags.dmrhSetAppRestrictions()) {
+ } else {
final boolean isRoleHolder;
if (who != null) {
// Caller is DO or PO. They cannot call this on parent
@@ -13299,19 +13275,6 @@
return bundle != null ? bundle : Bundle.EMPTY;
});
}
-
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && isCallerDelegate(caller,
- DELEGATION_APP_RESTRICTIONS)));
- return mInjector.binderWithCleanCallingIdentity(() -> {
- Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
- caller.getUserHandle());
- // if no restrictions were saved, mUserManager.getApplicationRestrictions
- // returns null, but DPM method should return an empty Bundle as per JavaDoc
- return bundle != null ? bundle : Bundle.EMPTY;
- });
}
}
@@ -14320,10 +14283,6 @@
return;
}
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
- }
-
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
synchronized (getLockObject()) {
int affectedUser = getAffectedUser(parent);
@@ -14840,7 +14799,8 @@
}
@Override
- public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled) {
+ public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled,
+ PersistableBundle options) {
Objects.requireNonNull(who, "ComponentName is null");
// Check can set secondary lockscreen enabled
@@ -14934,11 +14894,6 @@
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- for (String pkg : packages) {
- PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
- }
- }
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
@@ -15219,7 +15174,7 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- if (Flags.allowScreenBrightnessControlOnCope() && parent) {
+ if (parent) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
}
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING);
@@ -15230,7 +15185,7 @@
"Permission denial: device owners cannot update %1$s", setting));
}
int affectedUser;
- if (Flags.allowScreenBrightnessControlOnCope() && parent) {
+ if (parent) {
affectedUser = getProfileParentId(caller.getUserId());
} else {
affectedUser = caller.getUserId();
@@ -16822,13 +16777,11 @@
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
- if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
- final UserHandle user = UserHandle.of(userId);
- final String roleHolderPackage = getRoleHolderPackageNameOnUser(
- RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
- if (roleHolderPackage != null) {
- broadcastExplicitIntentToPackage(intent, roleHolderPackage, user);
- }
+ final UserHandle user = UserHandle.of(userId);
+ final String roleHolderPackage = getRoleHolderPackageNameOnUser(
+ RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
+ if (roleHolderPackage != null) {
+ broadcastExplicitIntentToPackage(intent, roleHolderPackage, user);
}
}
});
@@ -16836,18 +16789,10 @@
@Override
public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin, String callerPackage) {
- if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
- CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
- MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
- caller.getUserId());
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
- }
+ CallerIdentity caller = getCallerIdentity(admin, callerPackage);
+ enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
+ MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
+ caller.getUserId());
return mOwners.getSystemUpdateInfo();
}
@@ -17391,17 +17336,10 @@
@Nullable ComponentName componentName, @UserIdInt int callingUserId) {
synchronized (getLockObject()) {
int deviceOwnerUserId = -1;
- if (Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
- deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
- && getHeadlessDeviceOwnerModeForDeviceAdmin(componentName, callingUserId)
- == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
- ? UserHandle.USER_SYSTEM : callingUserId;
- } else {
- deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
- && getHeadlessDeviceOwnerModeForDeviceOwner()
- == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
- ? UserHandle.USER_SYSTEM : callingUserId;
- }
+ deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
+ && getHeadlessDeviceOwnerModeForDeviceAdmin(componentName, callingUserId)
+ == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
+ ? UserHandle.USER_SYSTEM : callingUserId;
Slogf.i(LOG_TAG, "Calling user %d, device owner will be set on user %d",
callingUserId, deviceOwnerUserId);
// hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb.
@@ -18700,19 +18638,16 @@
// Backup service has to be enabled on the main user in order for it to be enabled on
// secondary users.
- if (Flags.headlessSingleUserFixes() && isDeviceOwner(caller)
- && getHeadlessDeviceOwnerModeForDeviceOwner()
+ if (isDeviceOwner(caller) && getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
toggleBackupServiceActive(UserHandle.USER_SYSTEM, enabled);
}
toggleBackupServiceActive(caller.getUserId(), enabled);
- if (Flags.backupServiceSecurityLogEventEnabled()) {
- if (SecurityLog.isLoggingEnabled()) {
- SecurityLog.writeEvent(SecurityLog.TAG_BACKUP_SERVICE_TOGGLED,
- caller.getPackageName(), caller.getUserId(), enabled ? 1 : 0);
- }
+ if (SecurityLog.isLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_BACKUP_SERVICE_TOGGLED,
+ caller.getPackageName(), caller.getUserId(), enabled ? 1 : 0);
}
}
@@ -21442,13 +21377,7 @@
final CallerIdentity caller = getCallerIdentity(callerPackage);
- if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
- enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
- } else {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
- }
+ enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
caller.getUserId());
@@ -21937,18 +21866,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);
}
@@ -22047,16 +21973,9 @@
final long identity = Binder.clearCallingIdentity();
try {
boolean isSingleUserMode;
- if (Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
- int headlessDeviceOwnerMode = getHeadlessDeviceOwnerModeForDeviceAdmin(
- deviceAdmin, caller.getUserId());
- isSingleUserMode =
- headlessDeviceOwnerMode == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
- } else {
- isSingleUserMode =
- getHeadlessDeviceOwnerModeForDeviceOwner()
- == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
- }
+ int headlessDeviceOwnerMode = getHeadlessDeviceOwnerModeForDeviceAdmin(
+ deviceAdmin, caller.getUserId());
+ isSingleUserMode = headlessDeviceOwnerMode == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
if (Flags.headlessSingleMinTargetSdk()
&& mInjector.userManagerIsHeadlessSystemUserMode()
@@ -22455,35 +22374,17 @@
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
- if (!Flags.policyEngineMigrationV2Enabled()) {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
- "USB data signaling can only be controlled by a device owner or "
- + "a profile owner on an organization-owned device.");
+ synchronized (getLockObject()) {
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
+ caller.getPackageName(),
+ caller.getUserId());
Preconditions.checkState(canUsbDataSignalingBeDisabled(),
"USB data signaling cannot be disabled.");
- }
-
- synchronized (getLockObject()) {
- if (Flags.policyEngineMigrationV2Enabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
- caller.getPackageName(),
- caller.getUserId());
- Preconditions.checkState(canUsbDataSignalingBeDisabled(),
- "USB data signaling cannot be disabled.");
- mDevicePolicyEngine.setGlobalPolicy(
- PolicyDefinition.USB_DATA_SIGNALING,
- enforcingAdmin,
- new BooleanPolicyValue(enabled));
- } else {
- ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- if (admin.mUsbDataSignalingEnabled != enabled) {
- admin.mUsbDataSignalingEnabled = enabled;
- saveSettingsLocked(caller.getUserId());
- updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
- }
- }
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ enforcingAdmin,
+ new BooleanPolicyValue(enabled));
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING)
@@ -22505,24 +22406,10 @@
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
final CallerIdentity caller = getCallerIdentity(packageName);
- if (Flags.policyEngineMigrationV2Enabled()) {
- Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.USB_DATA_SIGNALING,
- caller.getUserId());
- return enabled == null || enabled;
- } else {
- synchronized (getLockObject()) {
- // If the caller is an admin, return the policy set by itself. Otherwise
- // return the device-wide policy.
- if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(
- caller)) {
- return getProfileOwnerOrDeviceOwnerLocked(
- caller.getUserId()).mUsbDataSignalingEnabled;
- } else {
- return isUsbDataSignalingEnabledInternalLocked();
- }
- }
- }
+ Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ caller.getUserId());
+ return enabled == null || enabled;
}
private boolean isUsbDataSignalingEnabledInternalLocked() {
@@ -24875,9 +24762,6 @@
@Override
public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- return;
- }
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
caller.getUserId());
@@ -24891,9 +24775,6 @@
@Override
public int getMaxPolicyStorageLimit(String callerPackageName) {
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- return -1;
- }
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
caller.getUserId());
@@ -24903,9 +24784,6 @@
@Override
public void forceSetMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- return;
- }
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
caller.getUserId());
@@ -24916,9 +24794,6 @@
@Override
public int getPolicySizeForAdmin(
String callerPackageName, android.app.admin.EnforcingAdmin admin) {
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- return -1;
- }
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
caller.getUserId());
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c5c371f..ab459df 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,7 +16,6 @@
package com.android.server;
-import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
@@ -38,6 +37,7 @@
import android.app.INotificationManager;
import android.app.SystemServiceRegistry;
import android.app.admin.DevicePolicySafetyChecker;
+import android.app.appfunctions.AppFunctionManagerConfiguration;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -1743,12 +1743,11 @@
mSystemServiceManager.startService(LogcatManagerService.class);
t.traceEnd();
- t.traceBegin("StartAppFunctionManager");
- if (enableAppFunctionManager()) {
+ if (AppFunctionManagerConfiguration.isSupported(context)) {
+ t.traceBegin("StartAppFunctionManager");
mSystemServiceManager.startService(AppFunctionManagerService.class);
+ t.traceEnd();
}
- t.traceEnd();
-
} catch (Throwable e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service");
@@ -2468,8 +2467,8 @@
reportWtf("starting RuntimeService", e);
}
t.traceEnd();
-
- if (!isWatch && !disableNetworkTime) {
+ if (!disableNetworkTime && (!isWatch || (isWatch
+ && android.server.Flags.allowNetworkTimeUpdateService()))) {
t.traceBegin("StartNetworkTimeUpdateService");
try {
networkTimeUpdater = new NetworkTimeUpdateService(context);
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 29f3871..ec74ef19 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -28,4 +28,11 @@
namespace: "wear_frameworks"
description: "Allow removing VpnManagerService"
bug: "340928692"
+}
+
+flag {
+ name: "allow_network_time_update_service"
+ namespace: "wear_systems"
+ description: "Allow NetworkTimeUpdateService on Wear"
+ bug: "327508176"
}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 7ed23cd..d2c91ff 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -888,7 +888,7 @@
val mayGrantByPrivileged =
!permission.isPrivileged ||
requestingPackageStates.anyIndexed { _, it ->
- checkPrivilegedPermissionAllowlist(it, permission)
+ checkPrivilegedPermissionAllowlistIfNeeded(it, permission)
}
val shouldGrantBySignature =
permission.isSignature &&
@@ -1280,7 +1280,16 @@
}
}
- private fun MutateStateScope.checkPrivilegedPermissionAllowlist(
+ /**
+ * We only check privileged permission allowlist for system privileged apps. Hence, for platform
+ * or for normal apps, we return true to indicate that we don't need to check the allowlist and
+ * will let follow-up checks to decide whether we should grant the permission.
+ *
+ * @return `true`, if the permission is allowlisted for system privileged apps, or if we
+ * don't need to check the allowlist (for platform or for normal apps).
+ * `false`, if the permission is not allowlisted for system privileged apps.
+ */
+ private fun MutateStateScope.checkPrivilegedPermissionAllowlistIfNeeded(
packageState: PackageState,
permission: Permission
): Boolean {
diff --git a/services/supervision/OWNERS b/services/supervision/OWNERS
new file mode 100644
index 0000000..e5f4147
--- /dev/null
+++ b/services/supervision/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/supervision/OWNERS
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index a4ef629..7ffd0ec 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -20,7 +20,9 @@
import android.annotation.Nullable;
import android.app.supervision.ISupervisionManager;
import android.content.Context;
-
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
@@ -28,7 +30,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-/** Service for handling system supervision. */
+/**
+ * Service for handling system supervision.
+ */
public class SupervisionService extends ISupervisionManager.Stub {
private static final String LOG_TAG = "SupervisionService";
@@ -44,8 +48,20 @@
}
@Override
- protected void dump(@NonNull FileDescriptor fd,
- @NonNull PrintWriter fout, @Nullable String[] args) {
+ public void onShellCommand(
+ @Nullable FileDescriptor in,
+ @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args,
+ @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ new SupervisionServiceShellCommand(this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ @Override
+ protected void dump(
+ @NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
fout.println("Supervision enabled: " + isSupervisionEnabled());
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
new file mode 100644
index 0000000..3aba24a
--- /dev/null
+++ b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
@@ -0,0 +1,61 @@
+/*
+ * 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.server.supervision;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+public class SupervisionServiceShellCommand extends ShellCommand {
+ private final SupervisionService mService;
+
+ public SupervisionServiceShellCommand(SupervisionService mService) {
+ this.mService = mService;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(null);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "help": return help(pw);
+ case "is-enabled": return isEnabled(pw);
+ default: return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int help(PrintWriter pw) {
+ pw.println("Supervision service commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text");
+ pw.println(" is-enabled");
+ pw.println(" Is supervision enabled");
+ return 0;
+ }
+
+ private int isEnabled(PrintWriter pw) {
+ pw.println(mService.isSupervisionEnabled());
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ help(getOutPrintWriter());
+ }
+}
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index a78972d..e6ff506 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -138,3 +138,17 @@
enabled: false,
},
}
+
+test_module_config {
+ name: "FrameworksInputMethodSystemServerTests_server_inputmethod",
+ base: "FrameworksInputMethodSystemServerTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.inputmethod"],
+}
+
+test_module_config {
+ name: "FrameworksImeTests_android_inputmethodservice",
+ base: "FrameworksImeTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.inputmethodservice"],
+}
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 6fd21f7..75db316 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -88,3 +88,10 @@
" && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" +
" && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)",
}
+
+test_module_config_host {
+ name: "PackageManagerServiceHostTests_test_overlayactorvisibilitytest",
+ base: "PackageManagerServiceHostTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.pm.test.OverlayActorVisibilityTest"],
+}
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index b8f1e83..24e931c 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -164,3 +164,25 @@
"done && " +
"$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
}
+
+test_module_config {
+ name: "PackageManagerServiceServerTests_server_pm_Presubmit",
+ base: "PackageManagerServiceServerTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.pm."],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+ name: "PackageManagerServiceServerTests_server_pm_Postsubmit",
+ base: "PackageManagerServiceServerTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.pm."],
+ include_annotations: ["android.platform.test.annotations.Postsubmit"],
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index c93f482..db88b16 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -47,3 +47,10 @@
platform_apis: true,
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "PackageManagerServiceUnitTests_verify_domain",
+ base: "PackageManagerServiceUnitTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.pm.test.verify.domain"],
+}
diff --git a/services/tests/appfunctions/Android.bp b/services/tests/appfunctions/Android.bp
new file mode 100644
index 0000000..b5cf986
--- /dev/null
+++ b/services/tests/appfunctions/Android.bp
@@ -0,0 +1,59 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FrameworksAppFunctionsTests",
+ team: "trendy_team_machine_learning",
+ defaults: [
+ "modules-utils-testable-device-config-defaults",
+ ],
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.ext.truth",
+ "platform-test-annotations",
+ "services.appfunctions",
+ "servicestests-core-utils",
+ "truth",
+ "frameworks-base-testutils",
+ "androidx.test.rules",
+ ],
+
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/appfunctions/AndroidManifest.xml b/services/tests/appfunctions/AndroidManifest.xml
new file mode 100644
index 0000000..1d42b17
--- /dev/null
+++ b/services/tests/appfunctions/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.appfunctionstests">
+
+ <application android:testOnly="true"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.appfunctionstests"
+ android:label="Frameworks AppFunctions Services Tests" />
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/appfunctions/AndroidTest.xml b/services/tests/appfunctions/AndroidTest.xml
new file mode 100644
index 0000000..0650120
--- /dev/null
+++ b/services/tests/appfunctions/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Frameworks AppFunctions Services Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="FrameworksAppFunctionsTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksAppFunctionsTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.appfunctionstests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/services/tests/appfunctions/OWNERS b/services/tests/appfunctions/OWNERS
new file mode 100644
index 0000000..7fa8917
--- /dev/null
+++ b/services/tests/appfunctions/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1627156
+include platform/frameworks/base:/core/java/android/app/appfunctions/OWNERS
diff --git a/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionRuntimeMetadataTest.kt b/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionRuntimeMetadataTest.kt
new file mode 100644
index 0000000..650e520
--- /dev/null
+++ b/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionRuntimeMetadataTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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 android.app.appfunctions
+
+import android.app.appsearch.AppSearchSchema
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class AppFunctionRuntimeMetadataTest {
+
+ @Test
+ fun getRuntimeSchemaNameForPackage() {
+ val actualSchemaName =
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage("com.example.app")
+
+ assertThat(actualSchemaName).isEqualTo("AppFunctionRuntimeMetadata-com.example.app")
+ }
+
+ @Test
+ fun testCreateChildRuntimeSchema() {
+ val runtimeSchema: AppSearchSchema =
+ AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema("com.example.app")
+
+ assertThat(runtimeSchema.schemaType).isEqualTo("AppFunctionRuntimeMetadata-com.example.app")
+ val propertyNameSet = runtimeSchema.properties.map { it.name }.toSet()
+ assertThat(propertyNameSet.contains(AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID))
+ .isTrue()
+ assertThat(propertyNameSet.contains(AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME))
+ .isTrue()
+ assertThat(propertyNameSet.contains(AppFunctionRuntimeMetadata.PROPERTY_ENABLED)).isTrue()
+ assertThat(
+ propertyNameSet.contains(
+ AppFunctionRuntimeMetadata.PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun testCreateParentRuntimeSchema() {
+ val runtimeSchema: AppSearchSchema =
+ AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema()
+
+ assertThat(runtimeSchema.schemaType).isEqualTo("AppFunctionRuntimeMetadata")
+ val propertyNameSet = runtimeSchema.properties.map { it.name }.toSet()
+ assertThat(propertyNameSet.contains(AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID))
+ .isTrue()
+ assertThat(propertyNameSet.contains(AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME))
+ .isTrue()
+ assertThat(propertyNameSet.contains(AppFunctionRuntimeMetadata.PROPERTY_ENABLED)).isTrue()
+ assertThat(
+ propertyNameSet.contains(
+ AppFunctionRuntimeMetadata.PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun testGetPackageNameFromSchema() {
+ val expectedPackageName = "com.foo.test"
+ val expectedPackageName2 = "com.bar.test"
+ val testSchemaType =
+ AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema(expectedPackageName)
+ .schemaType
+ val testSchemaType2 =
+ AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema(expectedPackageName2)
+ .schemaType
+
+ val actualPackageName = AppFunctionRuntimeMetadata.getPackageNameFromSchema(testSchemaType)
+ val actualPackageName2 =
+ AppFunctionRuntimeMetadata.getPackageNameFromSchema(testSchemaType2)
+
+ assertThat(actualPackageName).isEqualTo(expectedPackageName)
+ assertThat(actualPackageName2).isEqualTo(expectedPackageName2)
+ }
+
+ @Test
+ fun testGetPackageNameFromParentSchema() {
+ val expectedPackageName = AppFunctionRuntimeMetadata.APP_FUNCTION_INDEXER_PACKAGE
+ val testSchemaType =
+ AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema().schemaType
+
+ val actualPackageName = AppFunctionRuntimeMetadata.getPackageNameFromSchema(testSchemaType)
+
+ assertThat(actualPackageName).isEqualTo(expectedPackageName)
+ }
+}
diff --git a/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionStaticMetadataHelperTest.kt b/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionStaticMetadataHelperTest.kt
new file mode 100644
index 0000000..d8ce393
--- /dev/null
+++ b/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionStaticMetadataHelperTest.kt
@@ -0,0 +1,57 @@
+/*
+ * 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 android.app.appfunctions
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class AppFunctionStaticMetadataHelperTest {
+
+ @Test
+ fun getStaticSchemaNameForPackage() {
+ val actualSchemaName =
+ AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage("com.example.app")
+
+ assertThat(actualSchemaName).isEqualTo("AppFunctionStaticMetadata-com.example.app")
+ }
+
+ @Test
+ fun getDocumentIdForAppFunction() {
+ val packageName = "com.example.app"
+ val functionId = "someFunction"
+
+ val actualDocumentId =
+ AppFunctionStaticMetadataHelper.getDocumentIdForAppFunction(packageName, functionId)
+
+ assertThat(actualDocumentId).isEqualTo("com.example.app/someFunction")
+ }
+
+ @Test
+ fun getStaticMetadataQualifiedId() {
+ val packageName = "com.example.app"
+ val functionId = "someFunction"
+
+ val actualQualifiedId =
+ AppFunctionStaticMetadataHelper.getStaticMetadataQualifiedId(packageName, functionId)
+
+ assertThat(actualQualifiedId)
+ .isEqualTo("android\$apps-db/app_functions#com.example.app/someFunction")
+ }
+}
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
new file mode 100644
index 0000000..edcbb9e
--- /dev/null
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appfunctions
+
+import android.app.appfunctions.AppFunctionRuntimeMetadata
+import android.app.appfunctions.AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_NAMESPACE
+import android.app.appfunctions.AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema
+import android.app.appfunctions.AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema
+import android.app.appsearch.AppSearchBatchResult
+import android.app.appsearch.AppSearchManager
+import android.app.appsearch.GenericDocument
+import android.app.appsearch.GetByDocumentIdRequest
+import android.app.appsearch.PutDocumentsRequest
+import android.app.appsearch.RemoveByDocumentIdRequest
+import android.app.appsearch.SearchSpec
+import android.app.appsearch.SetSchemaRequest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class FutureAppSearchSessionTest {
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val appSearchManager = context.getSystemService(AppSearchManager::class.java)
+ private val testExecutor = MoreExecutors.directExecutor()
+
+ @Before
+ @After
+ fun clearData() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use {
+ val setSchemaRequest = SetSchemaRequest.Builder().setForceOverride(true).build()
+ it.setSchema(setSchemaRequest).get()
+ }
+ }
+
+ @Test
+ fun setSchema() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
+ )
+ .build()
+
+ val schema = session.setSchema(setSchemaRequest)
+
+ assertThat(schema.get()).isNotNull()
+ }
+ }
+
+ @Test
+ fun put() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
+ )
+ .build()
+ val schema = session.setSchema(setSchemaRequest)
+ assertThat(schema.get()).isNotNull()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_PACKAGE_NAME, TEST_FUNCTION_ID, "").build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+
+ val putResult = session.put(putDocumentsRequest)
+
+ assertThat(putResult.get().isSuccess).isTrue()
+ }
+ }
+
+ @Test
+ fun remove() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
+ )
+ .build()
+ val schema = session.setSchema(setSchemaRequest)
+ assertThat(schema.get()).isNotNull()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_PACKAGE_NAME, TEST_FUNCTION_ID, "").build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+ val putResult = session.put(putDocumentsRequest)
+ assertThat(putResult.get().isSuccess).isTrue()
+ val removeDocumentRequest =
+ RemoveByDocumentIdRequest.Builder(APP_FUNCTION_RUNTIME_NAMESPACE)
+ .addIds(appFunctionRuntimeMetadata.id)
+ .build()
+
+ val removeResult: AppSearchBatchResult<String, Void> =
+ session.remove(removeDocumentRequest).get()
+
+ assertThat(removeResult).isNotNull()
+ assertThat(removeResult.isSuccess).isTrue()
+ }
+ }
+
+ @Test
+ fun search() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
+ )
+ .build()
+ val schema = session.setSchema(setSchemaRequest)
+ assertThat(schema.get()).isNotNull()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_PACKAGE_NAME, TEST_FUNCTION_ID, "").build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+ val putResult = session.put(putDocumentsRequest)
+ assertThat(putResult.get().isSuccess).isTrue()
+
+ val searchResult = session.search("", SearchSpec.Builder().build())
+
+ val genericDocs =
+ searchResult.get().nextPage.get().stream().map { it.genericDocument }.toList()
+ assertThat(genericDocs).hasSize(1)
+ val foundAppFunctionRuntimeMetadata = AppFunctionRuntimeMetadata(genericDocs[0])
+ assertThat(foundAppFunctionRuntimeMetadata.functionId).isEqualTo(TEST_FUNCTION_ID)
+ }
+ }
+
+ @Test
+ fun getByDocumentId() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
+ )
+ .build()
+ session.setSchema(setSchemaRequest).get()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_PACKAGE_NAME, TEST_FUNCTION_ID, "").build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+ session.put(putDocumentsRequest)
+ val getRequest =
+ GetByDocumentIdRequest.Builder(APP_FUNCTION_RUNTIME_NAMESPACE)
+ .addIds(appFunctionRuntimeMetadata.id)
+ .build()
+
+ val genericDocument: GenericDocument? =
+ session.getByDocumentId(getRequest).get().successes[appFunctionRuntimeMetadata.id]
+
+ assertThat(genericDocument).isNotNull()
+ val foundAppFunctionRuntimeMetadata = AppFunctionRuntimeMetadata(genericDocument!!)
+ assertThat(foundAppFunctionRuntimeMetadata.functionId).isEqualTo(TEST_FUNCTION_ID)
+ }
+ }
+
+ private companion object {
+ const val TEST_DB: String = "test_db"
+ const val TEST_PACKAGE_NAME: String = "test_pkg"
+ const val TEST_FUNCTION_ID: String = "print"
+ }
+}
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/FutureGlobalSearchSessionTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureGlobalSearchSessionTest.kt
new file mode 100644
index 0000000..38cba65
--- /dev/null
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureGlobalSearchSessionTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appfunctions
+
+import android.app.appfunctions.AppFunctionRuntimeMetadata
+import android.app.appfunctions.AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema
+import android.app.appfunctions.AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema
+import android.app.appsearch.AppSearchManager
+import android.app.appsearch.AppSearchManager.SearchContext
+import android.app.appsearch.PutDocumentsRequest
+import android.app.appsearch.SetSchemaRequest
+import android.app.appsearch.observer.DocumentChangeInfo
+import android.app.appsearch.observer.ObserverCallback
+import android.app.appsearch.observer.ObserverSpec
+import android.app.appsearch.observer.SchemaChangeInfo
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.infra.AndroidFuture
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class FutureGlobalSearchSessionTest {
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val appSearchManager = context.getSystemService(AppSearchManager::class.java)
+ private val testExecutor = MoreExecutors.directExecutor()
+
+ @Before
+ @After
+ fun clearData() {
+ val searchContext = SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use {
+ val setSchemaRequest = SetSchemaRequest.Builder().setForceOverride(true).build()
+ it.setSchema(setSchemaRequest).get()
+ }
+ }
+
+ @Test
+ fun registerDocumentChangeObserverCallback() {
+ val packageObserverSpec: ObserverSpec =
+ ObserverSpec.Builder()
+ .addFilterSchemas(
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(TEST_TARGET_PKG_NAME)
+ )
+ .build()
+ val settableDocumentChangeInfo: AndroidFuture<DocumentChangeInfo> = AndroidFuture()
+ val observer: ObserverCallback =
+ object : ObserverCallback {
+ override fun onSchemaChanged(changeInfo: SchemaChangeInfo) {}
+
+ override fun onDocumentChanged(changeInfo: DocumentChangeInfo) {
+ settableDocumentChangeInfo.complete(changeInfo)
+ }
+ }
+ val futureGlobalSearchSession = FutureGlobalSearchSession(appSearchManager, testExecutor)
+
+ val registerPackageObserver: Void? =
+ futureGlobalSearchSession
+ .registerObserverCallbackAsync(
+ TEST_TARGET_PKG_NAME,
+ packageObserverSpec,
+ testExecutor,
+ observer,
+ )
+ .get()
+ assertThat(registerPackageObserver).isNull()
+ // Trigger document change
+ val searchContext = SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_TARGET_PKG_NAME),
+ )
+ .build()
+ val schema = session.setSchema(setSchemaRequest)
+ assertThat(schema.get()).isNotNull()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_TARGET_PKG_NAME, TEST_FUNCTION_ID, "")
+ .build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+ val putResult = session.put(putDocumentsRequest).get()
+ assertThat(putResult.isSuccess).isTrue()
+ }
+ assertThat(
+ settableDocumentChangeInfo
+ .get()
+ .changedDocumentIds
+ .contains(
+ AppFunctionRuntimeMetadata.getDocumentIdForAppFunction(
+ TEST_TARGET_PKG_NAME,
+ TEST_FUNCTION_ID,
+ )
+ )
+ )
+ .isTrue()
+ }
+
+ private companion object {
+ const val TEST_DB: String = "test_db"
+ const val TEST_TARGET_PKG_NAME = "com.android.frameworks.appfunctionstests"
+ const val TEST_FUNCTION_ID: String = "print"
+ }
+}
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
new file mode 100644
index 0000000..3ebf689
--- /dev/null
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appfunctions
+
+import android.app.appfunctions.AppFunctionRuntimeMetadata
+import android.app.appsearch.AppSearchManager
+import android.app.appsearch.AppSearchManager.SearchContext
+import android.app.appsearch.PutDocumentsRequest
+import android.app.appsearch.SetSchemaRequest
+import android.util.ArrayMap
+import android.util.ArraySet
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class MetadataSyncAdapterTest {
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val appSearchManager = context.getSystemService(AppSearchManager::class.java)
+ private val testExecutor = MoreExecutors.directExecutor()
+
+ @Before
+ @After
+ fun clearData() {
+ val searchContext = SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use {
+ val setSchemaRequest = SetSchemaRequest.Builder().setForceOverride(true).build()
+ it.setSchema(setSchemaRequest).get()
+ }
+ }
+
+ @Test
+ fun getPackageToFunctionIdMap() {
+ val searchContext: SearchContext = SearchContext.Builder(TEST_DB).build()
+ val functionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_TARGET_PKG_NAME, "testFunctionId", "").build()
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema())
+ .addSchemas(
+ AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema(TEST_TARGET_PKG_NAME)
+ )
+ .build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use {
+ val setSchemaResponse = it.setSchema(setSchemaRequest).get()
+ assertThat(setSchemaResponse).isNotNull()
+ val appSearchBatchResult = it.put(putDocumentsRequest).get()
+ assertThat(appSearchBatchResult.isSuccess).isTrue()
+ }
+
+ val metadataSyncAdapter =
+ MetadataSyncAdapter(
+ testExecutor,
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext),
+ )
+ val packageToFunctionIdMap =
+ metadataSyncAdapter.getPackageToFunctionIdMap(
+ AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE,
+ AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID,
+ AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME,
+ )
+
+ assertThat(packageToFunctionIdMap).isNotNull()
+ assertThat(packageToFunctionIdMap[TEST_TARGET_PKG_NAME]).containsExactly("testFunctionId")
+ }
+
+ @Test
+ fun getPackageToFunctionIdMap_multipleDocuments() {
+ val searchContext: SearchContext = SearchContext.Builder(TEST_DB).build()
+ val functionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_TARGET_PKG_NAME, "testFunctionId", "").build()
+ val functionRuntimeMetadata1 =
+ AppFunctionRuntimeMetadata.Builder(TEST_TARGET_PKG_NAME, "testFunctionId1", "").build()
+ val functionRuntimeMetadata2 =
+ AppFunctionRuntimeMetadata.Builder(TEST_TARGET_PKG_NAME, "testFunctionId2", "").build()
+ val functionRuntimeMetadata3 =
+ AppFunctionRuntimeMetadata.Builder(TEST_TARGET_PKG_NAME, "testFunctionId3", "").build()
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema())
+ .addSchemas(
+ AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema(TEST_TARGET_PKG_NAME)
+ )
+ .build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(
+ functionRuntimeMetadata,
+ functionRuntimeMetadata1,
+ functionRuntimeMetadata2,
+ functionRuntimeMetadata3,
+ )
+ .build()
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext).use {
+ val setSchemaResponse = it.setSchema(setSchemaRequest).get()
+ assertThat(setSchemaResponse).isNotNull()
+ val appSearchBatchResult = it.put(putDocumentsRequest).get()
+ assertThat(appSearchBatchResult.isSuccess).isTrue()
+ }
+
+ val metadataSyncAdapter =
+ MetadataSyncAdapter(
+ testExecutor,
+ FutureAppSearchSessionImpl(appSearchManager, testExecutor, searchContext),
+ )
+ val packageToFunctionIdMap =
+ metadataSyncAdapter.getPackageToFunctionIdMap(
+ AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE,
+ AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID,
+ AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME,
+ )
+
+ assertThat(packageToFunctionIdMap).isNotNull()
+ assertThat(packageToFunctionIdMap[TEST_TARGET_PKG_NAME])
+ .containsExactly(
+ "testFunctionId",
+ "testFunctionId1",
+ "testFunctionId2",
+ "testFunctionId3",
+ )
+ }
+
+ @Test
+ fun getAddedFunctionsDiffMap_noDiff() {
+ val staticPackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ staticPackageToFunctionMap.putAll(
+ mapOf(TEST_TARGET_PKG_NAME to ArraySet(setOf("testFunction1")))
+ )
+ val runtimePackageToFunctionMap: ArrayMap<String, ArraySet<String>> =
+ ArrayMap(staticPackageToFunctionMap)
+
+ val addedFunctionsDiffMap =
+ MetadataSyncAdapter.getAddedFunctionsDiffMap(
+ staticPackageToFunctionMap,
+ runtimePackageToFunctionMap,
+ )
+
+ assertThat(addedFunctionsDiffMap.isEmpty()).isEqualTo(true)
+ }
+
+ @Test
+ fun getAddedFunctionsDiffMap_addedFunction() {
+ val staticPackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ staticPackageToFunctionMap.putAll(
+ mapOf(TEST_TARGET_PKG_NAME to ArraySet(setOf("testFunction1", "testFunction2")))
+ )
+ val runtimePackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ runtimePackageToFunctionMap.putAll(
+ mapOf(TEST_TARGET_PKG_NAME to ArraySet(setOf("testFunction1")))
+ )
+
+ val addedFunctionsDiffMap =
+ MetadataSyncAdapter.getAddedFunctionsDiffMap(
+ staticPackageToFunctionMap,
+ runtimePackageToFunctionMap,
+ )
+
+ assertThat(addedFunctionsDiffMap.size).isEqualTo(1)
+ assertThat(addedFunctionsDiffMap[TEST_TARGET_PKG_NAME]).containsExactly("testFunction2")
+ }
+
+ @Test
+ fun getAddedFunctionsDiffMap_addedFunctionNewPackage() {
+ val staticPackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ staticPackageToFunctionMap.putAll(
+ mapOf(TEST_TARGET_PKG_NAME to ArraySet(setOf("testFunction1")))
+ )
+ val runtimePackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+
+ val addedFunctionsDiffMap =
+ MetadataSyncAdapter.getAddedFunctionsDiffMap(
+ staticPackageToFunctionMap,
+ runtimePackageToFunctionMap,
+ )
+
+ assertThat(addedFunctionsDiffMap.size).isEqualTo(1)
+ assertThat(addedFunctionsDiffMap[TEST_TARGET_PKG_NAME]).containsExactly("testFunction1")
+ }
+
+ @Test
+ fun getAddedFunctionsDiffMap_removedFunction() {
+ val staticPackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ val runtimePackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ runtimePackageToFunctionMap.putAll(
+ mapOf(TEST_TARGET_PKG_NAME to ArraySet(setOf("testFunction1")))
+ )
+
+ val addedFunctionsDiffMap =
+ MetadataSyncAdapter.getAddedFunctionsDiffMap(
+ staticPackageToFunctionMap,
+ runtimePackageToFunctionMap,
+ )
+
+ assertThat(addedFunctionsDiffMap.isEmpty()).isEqualTo(true)
+ }
+
+ @Test
+ fun getRemovedFunctionsDiffMap_noDiff() {
+ val staticPackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ staticPackageToFunctionMap.putAll(
+ mapOf(TEST_TARGET_PKG_NAME to ArraySet(setOf("testFunction1")))
+ )
+ val runtimePackageToFunctionMap: ArrayMap<String, ArraySet<String>> =
+ ArrayMap(staticPackageToFunctionMap)
+
+ val removedFunctionsDiffMap =
+ MetadataSyncAdapter.getRemovedFunctionsDiffMap(
+ staticPackageToFunctionMap,
+ runtimePackageToFunctionMap,
+ )
+
+ assertThat(removedFunctionsDiffMap.isEmpty()).isEqualTo(true)
+ }
+
+ @Test
+ fun getRemovedFunctionsDiffMap_removedFunction() {
+ val staticPackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ val runtimePackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ runtimePackageToFunctionMap.putAll(
+ mapOf(TEST_TARGET_PKG_NAME to ArraySet(setOf("testFunction1")))
+ )
+
+ val removedFunctionsDiffMap =
+ MetadataSyncAdapter.getRemovedFunctionsDiffMap(
+ staticPackageToFunctionMap,
+ runtimePackageToFunctionMap,
+ )
+
+ assertThat(removedFunctionsDiffMap.size).isEqualTo(1)
+ assertThat(removedFunctionsDiffMap[TEST_TARGET_PKG_NAME]).containsExactly("testFunction1")
+ }
+
+ @Test
+ fun getRemovedFunctionsDiffMap_addedFunction() {
+ val staticPackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+ staticPackageToFunctionMap.putAll(
+ mapOf(TEST_TARGET_PKG_NAME to ArraySet(setOf("testFunction1")))
+ )
+ val runtimePackageToFunctionMap: ArrayMap<String, ArraySet<String>> = ArrayMap()
+
+ val removedFunctionsDiffMap =
+ MetadataSyncAdapter.getRemovedFunctionsDiffMap(
+ staticPackageToFunctionMap,
+ runtimePackageToFunctionMap,
+ )
+
+ assertThat(removedFunctionsDiffMap.isEmpty()).isEqualTo(true)
+ }
+
+ private companion object {
+ const val TEST_DB: String = "test_db"
+ const val TEST_TARGET_PKG_NAME = "com.android.frameworks.appfunctionstests"
+ }
+}
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index f9441b2..fe73025 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -56,3 +56,13 @@
enabled: false,
},
}
+
+test_module_config {
+ name: "DisplayServiceTests_server_display",
+ base: "DisplayServiceTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.display"],
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
index 1f3184d..f589a2c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
@@ -42,31 +42,31 @@
private val mockToken = mock<IBinder>()
@Test
- fun `returns HBC max brightness if HBM supported and ON`() {
+ fun testMaxBrightness_HbmSupportedAndOn() {
val controller = createController()
assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS)
}
@Test
- fun `returns NBC max brightness if device does not support HBM`() {
+ fun testMaxBrightness_HbmNotSupported() {
val controller = createController(hbmSupported = false)
assertThat(controller.currentBrightnessMax).isEqualTo(NORMAL_BRIGHTNESS_LOW)
}
@Test
- fun `returns NBC max brightness if HBM not allowed`() {
+ fun testMaxBrightness_HbmNotAllowed() {
val controller = createController(hbmAllowed = false)
assertThat(controller.currentBrightnessMax).isEqualTo(NORMAL_BRIGHTNESS_LOW)
}
@Test
- fun `returns HBC max brightness if NBM is disabled`() {
+ fun testMaxBrightness_HbmDisabledAndNotAllowed() {
val controller = createController(nbmEnabled = false, hbmAllowed = false)
assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS)
}
@Test
- fun `returns HBC max brightness if lower than NBC max brightness`() {
+ fun testMaxBrightness_transitionPointLessThanCurrentNbmLimit() {
val controller = createController(
hbmAllowed = false,
hbmMaxBrightness = TRANSITION_POINT,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index f690b1b..2d4a29b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertEquals;
+import android.hardware.display.BrightnessInfo;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -112,7 +114,10 @@
.append("\n mBrightnessAdjustmentFlag:")
.append(displayBrightnessState.getBrightnessAdjustmentFlag())
.append("\n mIsUserInitiatedChange:")
- .append(displayBrightnessState.isUserInitiatedChange());
+ .append(displayBrightnessState.isUserInitiatedChange())
+ .append("\n mBrightnessMaxReason:")
+ .append(BrightnessInfo.briMaxReasonToString(
+ displayBrightnessState.getBrightnessMaxReason()));
return sb.toString();
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index d450683..fd05b26 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -263,7 +263,9 @@
mDisplayDeviceConfig.getPowerThrottlingConfigData();
assertNotNull(powerThrottlingConfigData);
assertEquals(0.1f, powerThrottlingConfigData.brightnessLowestCapAllowed, SMALL_DELTA);
- assertEquals(10, powerThrottlingConfigData.pollingWindowMillis);
+ assertEquals(15f, powerThrottlingConfigData.customAnimationRateSec, SMALL_DELTA);
+ assertEquals(20000, powerThrottlingConfigData.pollingWindowMaxMillis);
+ assertEquals(10000, powerThrottlingConfigData.pollingWindowMinMillis);
}
@Test
@@ -1295,7 +1297,9 @@
private String getPowerThrottlingConfig() {
return "<powerThrottlingConfig >\n"
+ "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n"
- + "<pollingWindowMillis>10</pollingWindowMillis>\n"
+ + "<customAnimationRateSec>15</customAnimationRateSec>\n"
+ + "<pollingWindowMaxMillis>20000</pollingWindowMaxMillis>\n"
+ + "<pollingWindowMinMillis>10000</pollingWindowMinMillis>\n"
+ "<powerThrottlingMap>\n"
+ "<powerThrottlingPoint>\n"
+ "<thermalStatus>light</thermalStatus>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 026fcc4..76c8e686 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -64,6 +64,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions.LaunchCookie;
import android.app.PropertyInvalidatedCache;
import android.companion.virtual.IVirtualDevice;
@@ -106,6 +107,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.MessageQueue;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -141,6 +143,7 @@
import com.android.server.display.DisplayManagerService.SyncRoot;
import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.layout.Layout;
import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
@@ -358,6 +361,7 @@
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Mock PackageManagerInternal mMockPackageManagerInternal;
@Mock DisplayManagerInternal mMockDisplayManagerInternal;
+ @Mock ActivityManagerInternal mMockActivityManagerInternal;
@Mock DisplayAdapter mMockDisplayAdapter;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@@ -388,6 +392,8 @@
PackageManagerInternal.class, mMockPackageManagerInternal);
mLocalServiceKeeperRule.overrideLocalService(
DisplayManagerInternal.class, mMockDisplayManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
+ ActivityManagerInternal.class, mMockActivityManagerInternal);
Display display = mock(Display.class);
when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
when(display.getBrightnessInfo()).thenReturn(mock(BrightnessInfo.class));
@@ -3049,6 +3055,74 @@
}
@Test
+ public void testBrightnessUpdates() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ initDisplayPowerController(localService);
+
+ final float invalidBrightness = -0.3f;
+ final float brightnessOff = -1.0f;
+ final float minimumBrightness = 0.0f;
+ final float validBrightness = 0.5f;
+
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+
+ // set and check valid brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, validBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(validBrightness,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // set and check invalid brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, invalidBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(PowerManager.BRIGHTNESS_MIN,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // reset and check valid brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, validBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(validBrightness,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // set and check brightness off
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, brightnessOff);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(PowerManager.BRIGHTNESS_MIN,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // reset and check valid brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, validBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(validBrightness,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // set and check minimum brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, minimumBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(PowerManager.BRIGHTNESS_MIN,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+ }
+
+ @Test
public void testResolutionChangeGetsBackedUp() throws Exception {
when(mMockFlags.isResolutionBackupRestoreEnabled()).thenReturn(true);
DisplayManagerService displayManager =
@@ -3128,6 +3202,45 @@
argThat(matchesFilter));
}
+ @Test
+ public void testOnDisplayChanged_HbmMetadataNull() {
+ DisplayPowerController dpc = mock(DisplayPowerController.class);
+ DisplayManagerService.Injector injector = new BasicInjector() {
+ @Override
+ DisplayPowerController getDisplayPowerController(Context context,
+ DisplayPowerController.Injector injector,
+ DisplayManagerInternal.DisplayPowerCallbacks callbacks, Handler handler,
+ SensorManager sensorManager, DisplayBlanker blanker,
+ LogicalDisplay logicalDisplay, BrightnessTracker brightnessTracker,
+ BrightnessSetting brightnessSetting, Runnable onBrightnessChangeRunnable,
+ HighBrightnessModeMetadata hbmMetadata, boolean bootCompleted,
+ DisplayManagerFlags flags) {
+ return dpc;
+ }
+ };
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, injector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ registerDefaultDisplays(displayManager);
+ displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ // Add the FakeDisplayDevice
+ FakeDisplayDevice displayDevice = new FakeDisplayDevice();
+ DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
+ displayDeviceInfo.state = Display.STATE_ON;
+ displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+ initDisplayPowerController(localService);
+
+ // Simulate DisplayDevice change
+ DisplayDeviceInfo displayDeviceInfo2 = new DisplayDeviceInfo();
+ displayDeviceInfo2.copyFrom(displayDeviceInfo);
+ displayDeviceInfo2.state = Display.STATE_DOZE;
+ updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo2);
+
+ verify(dpc).onDisplayChanged(/* hbmMetadata= */ null, Layout.NO_LEAD_DISPLAY);
+ }
+
private void initDisplayPowerController(DisplayManagerInternal localService) {
localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
@Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
index 30c384a..5e868a3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
@@ -38,7 +38,7 @@
private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
@Mock
- private DisplayPowerControllerInterface mDisplayPowerController;
+ private DisplayPowerController mDisplayPowerController;
private DisplayOffloadSessionImpl mSession;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 2166cb7..d0aec3b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -2587,7 +2587,7 @@
BrightnessClamperController getBrightnessClamperController(Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data, Context context,
- DisplayManagerFlags flags, SensorManager sensorManager) {
+ DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
return mClamperController;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
index 33d3020..a684fdb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
@@ -60,7 +60,7 @@
}
@Test
- fun `destroys ColorFade on stop`() {
+ fun destroysColorFadeOnStop() {
displayPowerState.stop()
val runnableCaptor = argumentCaptor<Runnable>()
@@ -71,13 +71,13 @@
}
@Test
- fun `GIVEN not prepared WHEN draw runnable is called THEN colorFade not drawn`() {
+ fun testColorFadeDraw_notPrepared() {
displayPowerState.mColorFadeDrawRunnable.run()
verify(mockColorFade, never()).draw(anyFloat())
}
@Test
- fun `GIVEN prepared WHEN draw runnable is called THEN colorFade is drawn`() {
+ fun testColorFadeDraw_prepared() {
displayPowerState.prepareColorFade(mockContext, ColorFade.MODE_FADE)
clearInvocations(mockColorFade)
@@ -87,7 +87,7 @@
}
@Test
- fun `GIVEN prepared AND stopped WHEN draw runnable is called THEN colorFade is not drawn`() {
+ fun testColorFadeDraw_preparedAndStopped() {
displayPowerState.prepareColorFade(mockContext, ColorFade.MODE_FADE)
clearInvocations(mockColorFade)
displayPowerState.stop()
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index a7e0ebd..120cc84 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -118,7 +118,7 @@
@Mock
private DisplayManagerFlags mFlags;
@Mock
- private DisplayPowerControllerInterface mMockedDisplayPowerController;
+ private DisplayPowerController mMockedDisplayPowerController;
private Handler mHandler;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 0ce9233..da79f30 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -31,7 +31,6 @@
import android.content.Context;
import android.hardware.SensorManager;
-import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.PowerManager;
@@ -161,12 +160,6 @@
}
@Test
- public void testMaxReasonIsNoneOnInit() {
- assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
- mClamperController.getBrightnessMaxReason());
- }
-
- @Test
public void testOnDisplayChanged_DelegatesToClamper() {
mClamperController.onDisplayChanged(mMockDisplayDeviceData);
@@ -233,7 +226,7 @@
float clampedBrightness = 0.6f;
float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
- when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.POWER);
when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(false);
mTestInjector.mCapturedChangeListener.onChanged();
@@ -257,7 +250,7 @@
float clampedBrightness = 0.6f;
float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
- when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.POWER);
when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
@@ -281,7 +274,7 @@
float clampedBrightness = 0.8f;
float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
- when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.POWER);
when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
@@ -305,7 +298,7 @@
float clampedBrightness = 0.6f;
float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
- when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.POWER);
when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
@@ -365,7 +358,7 @@
private BrightnessClamperController createBrightnessClamperController() {
return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener,
- mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager);
+ mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager, 0);
}
interface TestDisplayListenerModifier extends BrightnessStateModifier,
@@ -403,7 +396,7 @@
Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data,
- DisplayManagerFlags flags, Context context) {
+ DisplayManagerFlags flags, Context context, float currentBrightness) {
mCapturedChangeListener = clamperChangeListener;
return mClampers;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
index b3f33ad..c4898da 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.Temperature;
@@ -58,12 +59,18 @@
private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
new FakeDeviceConfigInterface();
private final TestHandler mTestHandler = new TestHandler(null);
+ private final TestInjector mTestInjector = new TestInjector();
private BrightnessPowerClamper mClamper;
+ private final float mCurrentBrightness = 0.6f;
+ private PowerChangeListener mPowerChangeListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mClamper = new BrightnessPowerClamper(new TestInjector(), mTestHandler,
- mMockClamperChangeListener, new TestPowerData());
+ mClamper = new BrightnessPowerClamper(mTestInjector, mTestHandler,
+ mMockClamperChangeListener, new TestPowerData(), mCurrentBrightness);
+ mPowerChangeListener = mClamper.getPowerChangeListener();
+ mPmicMonitor = mTestInjector.getPmicMonitor(mPowerChangeListener, null, 5, 10);
+ mPmicMonitor.setPowerChangeListener(mPowerChangeListener);
mTestHandler.flush();
}
@@ -79,36 +86,27 @@
}
@Test
- public void testPowerThrottlingNoOngoingAnimation() throws RemoteException {
- mPmicMonitor.setThermalStatus(Temperature.THROTTLING_SEVERE);
+ public void testPowerThrottlingWithThermalLevelLight() throws RemoteException {
+ mPmicMonitor.setThermalStatus(Temperature.THROTTLING_LIGHT);
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
// update a new device config for power-throttling.
mClamper.onDisplayChanged(new TestPowerData(
- List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 100f))));
+ List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_LIGHT, 100f))));
mPmicMonitor.setAvgPowerConsumed(200f);
float expectedBrightness = 0.5f;
- expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
+ expectedBrightness = expectedBrightness * mCurrentBrightness;
mTestHandler.flush();
// Assume current brightness as max, as there is no throttling.
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- mPmicMonitor.setThermalStatus(Temperature.THROTTLING_CRITICAL);
- // update a new device config for power-throttling.
- mClamper.onDisplayChanged(new TestPowerData(
- List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 50f))));
-
- mPmicMonitor.setAvgPowerConsumed(100f);
- expectedBrightness = 0.5f * PowerManager.BRIGHTNESS_MAX;
- mTestHandler.flush();
- assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
}
@Test
- public void testPowerThrottlingWithOngoingAnimation() throws RemoteException {
+ public void testPowerThrottlingWithThermalLevelSevere() throws RemoteException {
mPmicMonitor.setThermalStatus(Temperature.THROTTLING_SEVERE);
mTestHandler.flush();
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -119,20 +117,10 @@
mPmicMonitor.setAvgPowerConsumed(200f);
float expectedBrightness = 0.5f;
- expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
-
+ expectedBrightness = expectedBrightness * mCurrentBrightness;
mTestHandler.flush();
// Assume current brightness as max, as there is no throttling.
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- mPmicMonitor.setThermalStatus(Temperature.THROTTLING_CRITICAL);
- // update a new device config for power-throttling.
- mClamper.onDisplayChanged(new TestPowerData(
- List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 50f))));
-
- mPmicMonitor.setAvgPowerConsumed(100f);
- expectedBrightness = 0.5f * PowerManager.BRIGHTNESS_MAX;
- mTestHandler.flush();
- assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
}
@Test
@@ -148,8 +136,7 @@
mPmicMonitor.setAvgPowerConsumed(200f);
float expectedBrightness = 0.5f;
- expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
-
+ expectedBrightness = expectedBrightness * mCurrentBrightness;
mTestHandler.flush();
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -169,10 +156,11 @@
private static class TestPmicMonitor extends PmicMonitor {
private Temperature mCurrentTemperature;
- private final PowerChangeListener mListener;
- TestPmicMonitor(PowerChangeListener listener, int pollingTime) {
- super(listener, pollingTime);
- mListener = listener;
+ private PowerChangeListener mListener;
+ TestPmicMonitor(PowerChangeListener listener,
+ IThermalService thermalService,
+ int pollingTimeMax, int pollingTimeMin) {
+ super(listener, thermalService, pollingTimeMax, pollingTimeMin);
}
public void setAvgPowerConsumed(float power) {
int status = mCurrentTemperature.getStatus();
@@ -181,13 +169,18 @@
public void setThermalStatus(@Temperature.ThrottlingStatus int status) {
mCurrentTemperature = new Temperature(100, Temperature.TYPE_SKIN, "test_temp", status);
}
+ public void setPowerChangeListener(PowerChangeListener listener) {
+ mListener = listener;
+ }
}
private class TestInjector extends BrightnessPowerClamper.Injector {
@Override
TestPmicMonitor getPmicMonitor(PowerChangeListener listener,
- int pollingTime) {
- mPmicMonitor = new TestPmicMonitor(listener, pollingTime);
+ IThermalService thermalService,
+ int minPollingTimeMillis, int maxPollingTimeMillis) {
+ mPmicMonitor = new TestPmicMonitor(listener, thermalService, maxPollingTimeMillis,
+ minPollingTimeMillis);
return mPmicMonitor;
}
@@ -216,7 +209,7 @@
mUniqueDisplayId = uniqueDisplayId;
mDataId = dataId;
mData = PowerThrottlingData.create(data);
- mConfigData = new PowerThrottlingConfigData(0.1f, 10);
+ mConfigData = new PowerThrottlingConfigData(0.1f, 10, 20, 10);
}
@NonNull
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
deleted file mode 100644
index 9d16594..0000000
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display.brightness.clamper;
-
-import static com.android.server.display.config.DisplayDeviceConfigTestUtilsKt.createSensorData;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.hardware.display.DisplayManager;
-import android.os.IThermalEventListener;
-import android.os.IThermalService;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.Temperature;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.Keep;
-import com.android.server.display.DisplayDeviceConfig;
-import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
-import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
-import com.android.server.display.config.SensorData;
-import com.android.server.display.feature.DeviceConfigParameterProvider;
-import com.android.server.testutils.FakeDeviceConfigInterface;
-import com.android.server.testutils.TestHandler;
-
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@RunWith(JUnitParamsRunner.class)
-public class BrightnessThermalClamperTest {
-
- private static final float FLOAT_TOLERANCE = 0.001f;
-
- private static final String DISPLAY_ID = "displayId";
- @Mock
- private IThermalService mMockThermalService;
- @Mock
- private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
-
- private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
- new FakeDeviceConfigInterface();
- private final TestHandler mTestHandler = new TestHandler(null);
- private BrightnessThermalClamper mClamper;
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mClamper = new BrightnessThermalClamper(new TestInjector(), mTestHandler,
- mMockClamperChangeListener, new TestThermalData());
- mTestHandler.flush();
- }
-
- @Test
- public void testTypeIsThermal() {
- assertEquals(BrightnessClamper.Type.THERMAL, mClamper.getType());
- }
-
- @Test
- public void testNoThrottlingData() {
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- @Keep
- private static Object[][] testThrottlingData() {
- // throttlingLevels, throttlingStatus, expectedActive, expectedBrightness
- return new Object[][] {
- // no throttling data
- {List.of(), Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
- // throttlingStatus < min throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
- // throttlingStatus = min throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_MODERATE, true, 0.5f},
- // throttlingStatus between min and max throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_SEVERE, true, 0.5f},
- // throttlingStatus = max throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_CRITICAL, true, 0.1f},
- // throttlingStatus > max throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_EMERGENCY, true, 0.1f},
- };
- }
- @Test
- @Parameters(method = "testThrottlingData")
- public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
- @Temperature.ThrottlingStatus int throttlingStatus,
- boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
- mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
- mTestHandler.flush();
- assertEquals(expectedActive, mClamper.isActive());
- assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- @Test
- @Parameters(method = "testThrottlingData")
- public void testOnDisplayChangeAfterNotifyThrottling(List<ThrottlingLevel> throttlingLevels,
- @Temperature.ThrottlingStatus int throttlingStatus,
- boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
- thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
- mTestHandler.flush();
- assertEquals(expectedActive, mClamper.isActive());
- assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- @Test
- public void testOverrideData() throws RemoteException {
- IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
- thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- mClamper.onDisplayChanged(new TestThermalData(
- List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f))));
- mTestHandler.flush();
- assertTrue(mClamper.isActive());
- assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- overrideThrottlingData("displayId,1,emergency,0.4");
- mClamper.onDeviceConfigChanged();
- mTestHandler.flush();
-
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- overrideThrottlingData("displayId,1,moderate,0.4");
- mClamper.onDeviceConfigChanged();
- mTestHandler.flush();
-
- assertTrue(mClamper.isActive());
- assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- @Test
- public void testDisplaySensorBasedThrottling() throws RemoteException {
- final int severity = PowerManager.THERMAL_STATUS_SEVERE;
- IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
- // Update config to listen to display type sensor.
- final SensorData tempSensor = createSensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
- final TestThermalData thermalData =
- new TestThermalData(
- DISPLAY_ID,
- DisplayDeviceConfig.DEFAULT_ID,
- List.of(new ThrottlingLevel(severity, 0.5f)),
- tempSensor);
- mClamper.onDisplayChanged(thermalData);
- mTestHandler.flush();
- verify(mMockThermalService).unregisterThermalEventListener(thermalEventListener);
- thermalEventListener = captureThermalEventListener(Temperature.TYPE_DISPLAY);
- assertFalse(mClamper.isActive());
-
- // Verify no throttling triggered when any other sensor notification received.
- thermalEventListener.notifyThrottling(createSkinTemperature(severity));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
-
- thermalEventListener.notifyThrottling(createDisplayTemperature("OTHER-SENSOR", severity));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
-
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- // Verify throttling triggered when display sensor of given name throttled.
- thermalEventListener.notifyThrottling(createDisplayTemperature(tempSensor.name, severity));
- mTestHandler.flush();
- assertTrue(mClamper.isActive());
- assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- private IThermalEventListener captureSkinThermalEventListener() throws RemoteException {
- return captureThermalEventListener(Temperature.TYPE_SKIN);
- }
-
- private IThermalEventListener captureThermalEventListener(int type) throws RemoteException {
- ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
- IThermalEventListener.class);
- verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
- type));
- return captor.getValue();
- }
-
- private Temperature createDisplayTemperature(
- @NonNull String sensorName, @Temperature.ThrottlingStatus int status) {
- return new Temperature(100, Temperature.TYPE_DISPLAY, sensorName, status);
- }
-
- private Temperature createSkinTemperature(@Temperature.ThrottlingStatus int status) {
- return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
- }
-
- private void overrideThrottlingData(String data) {
- mFakeDeviceConfigInterface.putProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, data);
- }
-
- private class TestInjector extends BrightnessThermalClamper.Injector {
- @Override
- IThermalService getThermalService() {
- return mMockThermalService;
- }
-
- @Override
- DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
- return new DeviceConfigParameterProvider(mFakeDeviceConfigInterface);
- }
- }
-
- private static class TestThermalData implements BrightnessThermalClamper.ThermalData {
-
- private final String mUniqueDisplayId;
- private final String mDataId;
- private final ThermalBrightnessThrottlingData mData;
- private final SensorData mTempSensor;
-
- private TestThermalData() {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null,
- SensorData.loadTempSensorUnspecifiedConfig());
- }
-
- private TestThermalData(List<ThrottlingLevel> data) {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data,
- SensorData.loadTempSensorUnspecifiedConfig());
- }
-
- private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data,
- SensorData tempSensor) {
- mUniqueDisplayId = uniqueDisplayId;
- mDataId = dataId;
- mData = ThermalBrightnessThrottlingData.create(data);
- mTempSensor = tempSensor;
- }
-
- @NonNull
- @Override
- public String getUniqueDisplayId() {
- return mUniqueDisplayId;
- }
-
- @NonNull
- @Override
- public String getThermalThrottlingDataId() {
- return mDataId;
- }
-
- @Nullable
- @Override
- public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
- return mData;
- }
-
- @NonNull
- @Override
- public SensorData getTempSensor() {
- return mTempSensor;
- }
- }
-}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalModifierTest.java
new file mode 100644
index 0000000..35d384b
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalModifierTest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import static com.android.server.display.config.DisplayDeviceConfigTestUtilsKt.createSensorData;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.IBinder;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.Temperature;
+import android.provider.DeviceConfig;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.Keep;
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
+import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.clamper.BrightnessClamperController.ModifiersAggregatedState;
+import com.android.server.display.config.SensorData;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.testutils.FakeDeviceConfigInterface;
+import com.android.server.testutils.TestHandler;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(JUnitParamsRunner.class)
+public class BrightnessThermalModifierTest {
+ private static final int NO_MODIFIER = 0;
+
+ private static final float FLOAT_TOLERANCE = 0.001f;
+
+ private static final String DISPLAY_ID = "displayId";
+ @Mock
+ private IThermalService mMockThermalService;
+ @Mock
+ private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
+ @Mock
+ private DisplayManagerInternal.DisplayPowerRequest mMockRequest;
+ @Mock
+ private DisplayDeviceConfig mMockDisplayDeviceConfig;
+ @Mock
+ private IBinder mMockBinder;
+
+ private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
+ new FakeDeviceConfigInterface();
+ private final TestHandler mTestHandler = new TestHandler(null);
+ private BrightnessThermalModifier mModifier;
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockDisplayDeviceConfig.getTempSensor())
+ .thenReturn(SensorData.loadTempSensorUnspecifiedConfig());
+ mModifier = new BrightnessThermalModifier(new TestInjector(), mTestHandler,
+ mMockClamperChangeListener,
+ ClamperTestUtilsKt.createDisplayDeviceData(mMockDisplayDeviceConfig, mMockBinder));
+ mTestHandler.flush();
+ }
+
+
+ @Test
+ public void testNoThrottlingData() {
+ assertModifierState(
+ 0.3f, true,
+ PowerManager.BRIGHTNESS_MAX, 0.3f,
+ false, true);
+ }
+
+ @Keep
+ private static Object[][] testThrottlingData() {
+ // throttlingLevels, throttlingStatus, expectedActive, expectedBrightness
+ return new Object[][] {
+ // no throttling data
+ {List.of(), Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
+ // throttlingStatus < min throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
+ // throttlingStatus = min throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_MODERATE, true, 0.5f},
+ // throttlingStatus between min and max throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_SEVERE, true, 0.5f},
+ // throttlingStatus = max throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_CRITICAL, true, 0.1f},
+ // throttlingStatus > max throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_EMERGENCY, true, 0.1f},
+ };
+ }
+ @Test
+ @Parameters(method = "testThrottlingData")
+ public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
+ @Temperature.ThrottlingStatus int throttlingStatus,
+ boolean expectedActive, float expectedBrightness) throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ onDisplayChange(throttlingLevels);
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ expectedBrightness, expectedBrightness,
+ expectedActive, !expectedActive);
+ }
+
+ @Test
+ @Parameters(method = "testThrottlingData")
+ public void testOnDisplayChangeAfterNotifyThrottling(List<ThrottlingLevel> throttlingLevels,
+ @Temperature.ThrottlingStatus int throttlingStatus,
+ boolean expectedActive, float expectedBrightness) throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ onDisplayChange(throttlingLevels);
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ expectedBrightness, expectedBrightness,
+ expectedActive, !expectedActive);
+ }
+
+ @Test
+ public void testAppliesFastChangeOnlyOnActivation() throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ onDisplayChange(List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f)));
+ mTestHandler.flush();
+
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
+ mTestHandler.flush();
+
+ // expectedSlowChange = false
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.5f, 0.5f,
+ true, false);
+
+ // slowChange is unchanged
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.5f, 0.5f,
+ true, true);
+ }
+
+ @Test
+ public void testCapsMaxBrightnessOnly_currentBrightnessIsLowAndFastChange()
+ throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ onDisplayChange(List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f)));
+ mTestHandler.flush();
+
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
+ mTestHandler.flush();
+
+ assertModifierState(
+ 0.1f, false,
+ 0.5f, 0.1f,
+ true, false);
+ }
+
+ @Test
+ public void testOverrideData() throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ onDisplayChange(List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f)));
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.5f, 0.5f,
+ true, false);
+
+ overrideThrottlingData("displayId,1,emergency,0.4");
+ mModifier.onDeviceConfigChanged();
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ overrideThrottlingData("displayId,1,moderate,0.4");
+ mModifier.onDeviceConfigChanged();
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.4f, 0.4f,
+ true, false);
+ }
+
+ @Test
+ public void testDisplaySensorBasedThrottling() throws RemoteException {
+ final int severity = PowerManager.THERMAL_STATUS_SEVERE;
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ // Update config to listen to display type sensor.
+ SensorData tempSensor = createSensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+
+ when(mMockDisplayDeviceConfig.getTempSensor()).thenReturn(tempSensor);
+ onDisplayChange(List.of(new ThrottlingLevel(severity, 0.5f)));
+ mTestHandler.flush();
+
+ verify(mMockThermalService).unregisterThermalEventListener(thermalEventListener);
+ thermalEventListener = captureThermalEventListener(Temperature.TYPE_DISPLAY);
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ // Verify no throttling triggered when any other sensor notification received.
+ thermalEventListener.notifyThrottling(createSkinTemperature(severity));
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ thermalEventListener.notifyThrottling(createDisplayTemperature("OTHER-SENSOR", severity));
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ // Verify throttling triggered when display sensor of given name throttled.
+ thermalEventListener.notifyThrottling(createDisplayTemperature(tempSensor.name, severity));
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.5f, 0.5f,
+ true, false);
+ }
+
+ private IThermalEventListener captureSkinThermalEventListener() throws RemoteException {
+ return captureThermalEventListener(Temperature.TYPE_SKIN);
+ }
+
+ private IThermalEventListener captureThermalEventListener(int type) throws RemoteException {
+ ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
+ IThermalEventListener.class);
+ verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
+ type));
+ return captor.getValue();
+ }
+
+ private Temperature createDisplayTemperature(
+ @NonNull String sensorName, @Temperature.ThrottlingStatus int status) {
+ return new Temperature(100, Temperature.TYPE_DISPLAY, sensorName, status);
+ }
+
+ private Temperature createSkinTemperature(@Temperature.ThrottlingStatus int status) {
+ return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
+ }
+
+ private void overrideThrottlingData(String data) {
+ mFakeDeviceConfigInterface.putProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, data);
+ }
+
+ private void onDisplayChange(List<ThrottlingLevel> throttlingLevels) {
+ Map<String, ThermalBrightnessThrottlingData> throttlingLevelsMap = new HashMap<>();
+ throttlingLevelsMap.put(DisplayDeviceConfig.DEFAULT_ID,
+ ThermalBrightnessThrottlingData.create(throttlingLevels));
+ when(mMockDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId())
+ .thenReturn(throttlingLevelsMap);
+ mModifier.onDisplayChanged(ClamperTestUtilsKt.createDisplayDeviceData(
+ mMockDisplayDeviceConfig, mMockBinder, DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID));
+ }
+
+ private void assertModifierState(
+ float currentBrightness,
+ boolean currentSlowChange,
+ float maxBrightness, float brightness,
+ boolean isActive,
+ boolean isSlowChange) {
+ ModifiersAggregatedState modifierState = new ModifiersAggregatedState();
+ DisplayBrightnessState.Builder stateBuilder = DisplayBrightnessState.builder();
+ stateBuilder.setBrightness(currentBrightness);
+ stateBuilder.setIsSlowChange(currentSlowChange);
+
+ int maxBrightnessReason = isActive ? BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL
+ : BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+ int modifier = isActive ? BrightnessReason.MODIFIER_THROTTLED : NO_MODIFIER;
+
+ mModifier.applyStateChange(modifierState);
+ assertThat(modifierState.mMaxBrightness).isEqualTo(maxBrightness);
+ assertThat(modifierState.mMaxBrightnessReason).isEqualTo(maxBrightnessReason);
+
+ mModifier.apply(mMockRequest, stateBuilder);
+
+ assertThat(stateBuilder.getMaxBrightness()).isWithin(FLOAT_TOLERANCE).of(maxBrightness);
+ assertThat(stateBuilder.getBrightness()).isWithin(FLOAT_TOLERANCE).of(brightness);
+ assertThat(stateBuilder.getBrightnessMaxReason()).isEqualTo(maxBrightnessReason);
+ assertThat(stateBuilder.getBrightnessReason().getModifier()).isEqualTo(modifier);
+ assertThat(stateBuilder.isSlowChange()).isEqualTo(isSlowChange);
+ }
+
+
+ private class TestInjector extends BrightnessThermalModifier.Injector {
+ @Override
+ IThermalService getThermalService() {
+ return mMockThermalService;
+ }
+
+ @Override
+ DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
+ return new DeviceConfigParameterProvider(mFakeDeviceConfigInterface);
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/ClamperTestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/ClamperTestUtils.kt
index 5fd848f..f21749e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/ClamperTestUtils.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/ClamperTestUtils.kt
@@ -21,6 +21,7 @@
import com.android.server.display.DisplayDeviceConfig
import com.android.server.display.brightness.clamper.BrightnessClamperController.DisplayDeviceData
+@JvmOverloads
fun createDisplayDeviceData(
displayDeviceConfig: DisplayDeviceConfig,
displayToken: IBinder,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
index 34c6ba9..1f3f19f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
@@ -50,7 +50,7 @@
}
@Test
- fun `test app request votes`(@TestParameter testCase: AppRequestTestCase) {
+ fun testAppRequestVotes(@TestParameter testCase: AppRequestTestCase) {
whenever(mockFlags.ignoreAppPreferredRefreshRateRequest())
.thenReturn(testCase.ignoreRefreshRateRequest)
val displayModeDirector = DisplayModeDirector(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
index bf2edfe..3841211 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
@@ -39,7 +39,7 @@
}
@Test
- fun `updates summary with base mode refresh rate if not set`() {
+ fun updatesSummary_doesNotUpdateSummary_baseModeRefreshRateNotSet() {
val summary = createVotesSummary()
baseModeVote.updateSummary(summary)
@@ -48,7 +48,7 @@
}
@Test
- fun `keeps summary base mode refresh rate if set`() {
+ fun doesNotUpdateSummary_baseModeRefreshRateSet() {
val summary = createVotesSummary()
summary.appRequestBaseModeRefreshRate = OTHER_BASE_REFRESH_RATE
@@ -58,7 +58,7 @@
}
@Test
- fun `keeps summary with base mode refresh rate if vote refresh rate is negative`() {
+ fun doesNotUpdateSummary_baseModeRefreshRateNotSet_requestedRefreshRateInvalid() {
val invalidBaseModeVote = BaseModeRefreshRateVote(-10f)
val summary = createVotesSummary()
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
index 209e5a3..0a3c285 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
@@ -45,7 +45,7 @@
}
@Test
- fun `delegates update to children`() {
+ fun delegatesUpdateToChildren() {
val summary = createVotesSummary()
combinedVote.updateSummary(summary)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
index 38782c2..5b5ae65 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
@@ -28,7 +28,7 @@
class DisableRefreshRateSwitchingVoteTest {
@Test
- fun `disabled refresh rate switching is not changed`(
+ fun testDisableRefreshRateSwitch_alreadyDisabled(
@TestParameter voteDisableSwitching: Boolean
) {
val summary = createVotesSummary()
@@ -41,7 +41,7 @@
}
@Test
- fun `disables refresh rate switching if requested`() {
+ fun disablesRefreshRateSwitch_notDisabled_requested() {
val summary = createVotesSummary()
val vote = DisableRefreshRateSwitchingVote(true)
@@ -51,7 +51,7 @@
}
@Test
- fun `does not disable refresh rate switching if not requested`() {
+ fun doesNotDisableRefreshRateSwitch_notDisabled_notRequested() {
val summary = createVotesSummary()
val vote = DisableRefreshRateSwitchingVote(false)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
index 9edcc32..0968edb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
@@ -37,7 +37,7 @@
}
@Test
- fun `updates minPhysicalRefreshRate if summary has less`() {
+ fun updatesMinPhysicalRefreshRateWithBiggerValue() {
val summary = createVotesSummary()
summary.minPhysicalRefreshRate = 45f
@@ -47,7 +47,7 @@
}
@Test
- fun `does not update minPhysicalRefreshRate if summary has more`() {
+ fun doesNotUpdateMinPhysicalRefreshRateWithSmallerValue() {
val summary = createVotesSummary()
summary.minPhysicalRefreshRate = 75f
@@ -57,7 +57,7 @@
}
@Test
- fun `updates maxPhysicalRefreshRate if summary has more`() {
+ fun updatesMaxPhysicalRefreshRateWithSmallerValue() {
val summary = createVotesSummary()
summary.maxPhysicalRefreshRate = 120f
@@ -67,7 +67,7 @@
}
@Test
- fun `does not update maxPhysicalRefreshRate if summary has less`() {
+ fun doesNotUpdateMaxPhysicalRefreshRateWithBiggerValue() {
val summary = createVotesSummary()
summary.maxPhysicalRefreshRate = 75f
@@ -77,7 +77,7 @@
}
@Test
- fun `updates maxRenderFrameRate if summary has more`() {
+ fun updatesMaxRenderFrameRateWithSmallerValue() {
val summary = createVotesSummary()
summary.maxRenderFrameRate = 120f
@@ -87,7 +87,7 @@
}
@Test
- fun `does not update maxRenderFrameRate if summary has less`() {
+ fun doesNotUpdateMaxRenderFrameRateWithBiggerValue() {
val summary = createVotesSummary()
summary.maxRenderFrameRate = 75f
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
index 2d65f1c..9fa1e1b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
@@ -38,7 +38,7 @@
}
@Test
- fun `updates minRenderFrameRate if summary has less`() {
+ fun updatesMinRenderFrameRateWithBiggerValue() {
val summary = createVotesSummary()
summary.minRenderFrameRate = 45f
@@ -48,7 +48,7 @@
}
@Test
- fun `does not update minRenderFrameRate if summary has more`() {
+ fun doesNotUpdateMinRenderFrameRateWithSmallerValue() {
val summary = createVotesSummary()
summary.minRenderFrameRate = 75f
@@ -58,7 +58,7 @@
}
@Test
- fun `updates maxRenderFrameRate if summary has more`() {
+ fun updatesMaxPRenderFrameRateWithSmallerValue() {
val summary = createVotesSummary()
summary.maxRenderFrameRate = 120f
@@ -68,7 +68,7 @@
}
@Test
- fun `does not update maxRenderFrameRate if summary has less`() {
+ fun doesNotUpdateMaxPRenderFrameRateWithBiggerValue() {
val summary = createVotesSummary()
summary.maxRenderFrameRate = 75f
@@ -78,7 +78,7 @@
}
@Test
- fun `updates minPhysicalRefreshRate if summary has less`() {
+ fun updatesMinPhysicalRefreshRateWithBiggerValue() {
val summary = createVotesSummary()
summary.minPhysicalRefreshRate = 45f
@@ -88,7 +88,7 @@
}
@Test
- fun `does not update minPhysicalRefreshRate if summary has more`() {
+ fun doesNotUpdateMinPhysicalRefreshRateWithSmallerValue() {
val summary = createVotesSummary()
summary.minPhysicalRefreshRate = 75f
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
index dbe9e4a..be9c563 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
@@ -28,7 +28,7 @@
class RequestedRefreshRateVoteTest {
@Test
- fun `updates requestedRefreshRates`() {
+ fun testUpdatesRequestedRefreshRates() {
val refreshRate = 90f
val vote = RequestedRefreshRateVote(refreshRate)
val summary = createVotesSummary()
@@ -40,7 +40,7 @@
}
@Test
- fun `updates requestedRefreshRates with multiple refresh rates`() {
+ fun testUpdatesRequestedRefreshRates_multipleVotes() {
val refreshRate1 = 90f
val vote1 = RequestedRefreshRateVote(refreshRate1)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 4fc574a..d7dcca7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -103,7 +103,7 @@
}
@Test
- fun `test low power mode`(@TestParameter testCase: LowPowerTestCase) {
+ fun testLowPowerMode(@TestParameter testCase: LowPowerTestCase) {
whenever(mockFlags.isVsyncLowPowerVoteEnabled).thenReturn(testCase.vsyncLowPowerVoteEnabled)
whenever(spyContext.contentResolver)
.thenReturn(settingsProviderRule.mockContentResolver(null))
@@ -151,7 +151,7 @@
}
@Test
- fun `test settings refresh rates`(@TestParameter testCase: SettingsRefreshRateTestCase) {
+ fun testSettingsRefreshRates(@TestParameter testCase: SettingsRefreshRateTestCase) {
whenever(mockFlags.isPeakRefreshRatePhysicalLimitEnabled)
.thenReturn(testCase.peakRefreshRatePhysicalLimitEnabled)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
index 1be2fbf..319c21e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
@@ -39,7 +39,7 @@
}
@Test
- fun `updates size if width and height not set and display resolution voting disabled`() {
+ fun updatesSize_widthAndHeightNotSet_resolutionVotingDisabled() {
val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
summary.width = Vote.INVALID_SIZE
summary.height = Vote.INVALID_SIZE
@@ -55,7 +55,7 @@
}
@Test
- fun `does not update size if width set and display resolution voting disabled`() {
+ fun doesNotUpdateSiz_widthSet_resolutionVotingDisabled() {
val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
summary.width = 150
summary.height = Vote.INVALID_SIZE
@@ -71,7 +71,7 @@
}
@Test
- fun `does not update size if height set and display resolution voting disabled`() {
+ fun doesNotUpdateSize_heightSet_resolutionVotingDisabled() {
val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
summary.width = Vote.INVALID_SIZE
summary.height = 250
@@ -87,7 +87,7 @@
}
@Test
- fun `updates width if summary has more and display resolution voting enabled`() {
+ fun updatesWidthWithSmallerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 850
@@ -97,7 +97,7 @@
}
@Test
- fun `does not update width if summary has less and display resolution voting enabled`() {
+ fun doesNotUpdateWidthWithBiggerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 750
@@ -107,7 +107,7 @@
}
@Test
- fun `updates height if summary has more and display resolution voting enabled`() {
+ fun updatesHeightWithSmallerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.height = 1650
@@ -117,7 +117,7 @@
}
@Test
- fun `does not update height if summary has less and display resolution voting enabled`() {
+ fun doesNotUpdateHeightWithBiggerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.height = 1550
@@ -127,7 +127,7 @@
}
@Test
- fun `updates minWidth if summary has less and display resolution voting enabled`() {
+ fun updatesMinWidthWithSmallerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 150
summary.minWidth = 350
@@ -138,7 +138,7 @@
}
@Test
- fun `does not update minWidth if summary has more and display resolution voting enabled`() {
+ fun doesNotUpdateMinWidthWithBiggerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 150
summary.minWidth = 450
@@ -149,7 +149,7 @@
}
@Test
- fun `updates minHeight if summary has less and display resolution voting enabled`() {
+ fun updatesMinHeightWithSmallerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 150
summary.minHeight = 1150
@@ -160,7 +160,7 @@
}
@Test
- fun `does not update minHeight if summary has more and display resolution voting enabled`() {
+ fun doesNotUpdateMinHeightWithBiggerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 150
summary.minHeight = 1250
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
index 6ce49b8..2a50a33 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
@@ -39,7 +39,7 @@
}
@Test
- fun `adds supported mode ids if supportedModeIds in summary is null`() {
+ fun addsSupportedModeIds_summaryHasNull() {
val summary = createVotesSummary()
supportedModesVote.updateSummary(summary)
@@ -48,7 +48,7 @@
}
@Test
- fun `does not add supported mode ids if summary has empty list of modeIds`() {
+ fun doesNotAddSupportedModeIdes_summaryHasEmptyList() {
val summary = createVotesSummary()
summary.supportedModeIds = ArrayList()
@@ -58,7 +58,7 @@
}
@Test
- fun `filters out modes that does not match vote`() {
+ fun filtersModeIdsThatDoesNotMatchVote() {
val summary = createVotesSummary()
summary.supportedModeIds = ArrayList(listOf(otherMode, supportedModes[0]))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
index d0c112b..0da6885 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
@@ -42,7 +42,7 @@
}
@Test
- fun `adds supported refresh rates if supportedModes in summary is null`() {
+ fun addsSupportedRefreshRates_summaryHasNull() {
val summary = createVotesSummary()
supportedRefreshRatesVote.updateSummary(summary)
@@ -51,7 +51,7 @@
}
@Test
- fun `does not add supported refresh rates if summary has empty list of refresh rates`() {
+ fun doesNotAddSupportedRefreshRates_summaryHasEmptyList() {
val summary = createVotesSummary()
summary.supportedRefreshRates = ArrayList()
@@ -61,7 +61,7 @@
}
@Test
- fun `filters out supported refresh rates that does not match vote`() {
+ fun filtersSupportedRefreshRatesThatDoesNotMatchVote() {
val summary = createVotesSummary()
summary.supportedRefreshRates = ArrayList(listOf(otherMode, refreshRates[0]))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
index 5cd3a33..b2d83d7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
@@ -41,7 +41,7 @@
private val mockConfig = mock<DisplayDeviceConfig>()
@Test
- fun `test app supported modes`(@TestParameter testCase: AppSupportedModesTestCase) {
+ fun testAppSupportedModes(@TestParameter testCase: AppSupportedModesTestCase) {
whenever(mockFlags.isSynthetic60HzModesEnabled).thenReturn(testCase.syntheticModesEnabled)
whenever(mockConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
val syntheticModeManager = SyntheticModeManager(mockFlags)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
index c49205b..9ea7ea7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
@@ -51,7 +51,7 @@
private val storage = VotesStorage({}, null)
@Test
- fun `requestDisplayModes adds vote to storage`() {
+ fun testRequestDisplayModes_voteAdded() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -69,7 +69,7 @@
}
@Test
- fun `requestDisplayModes overrides votes in storage`() {
+ fun testRequestDisplayModes_voteReplaced() {
val systemRequestObserver = SystemRequestObserver(storage)
systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, intArrayOf(1, 2, 3))
@@ -89,7 +89,7 @@
}
@Test
- fun `requestDisplayModes removes vote to storage`() {
+ fun testRequestDisplayModes_voteRemoved() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -101,7 +101,7 @@
}
@Test
- fun `requestDisplayModes calls linkToDeath to token`() {
+ fun testTokenLinkToDeath() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -111,7 +111,7 @@
}
@Test
- fun `does not add votes to storage if binder died when requestDisplayModes called`() {
+ fun testBinderDied_voteRemoved() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -123,7 +123,7 @@
}
@Test
- fun `removes all votes from storage when binder dies`() {
+ fun testBinderDied_allVotesRemoved() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -138,7 +138,7 @@
}
@Test
- fun `calls unlinkToDeath on token when no votes remaining`() {
+ fun testTokenUnlinkToDeath_noMoreVotes() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -149,7 +149,7 @@
}
@Test
- fun `does not call unlinkToDeath on token when votes for other display in storage`() {
+ fun testTokenUnlinkToDeathNotCalled_votesForOtherDisplayInStorage() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -161,7 +161,7 @@
}
@Test
- fun `requestDisplayModes subset modes from different tokens`() {
+ fun testRequestDisplayModes_differentToken_voteHasModesSubset() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
@@ -187,7 +187,7 @@
}
@Test
- fun `recalculates vote if one binder dies`() {
+ fun testBinderDies_recalculatesVotes() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index dd5e1be..239e59b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -80,7 +80,7 @@
}
@Test
- fun `filters modes for summary supportedRefreshRates`(
+ fun testFiltersModes_supportedRefreshRates(
@TestParameter testCase: SupportedRefreshRatesTestCase
) {
val summary = createSummary(testCase.supportedModesVoteEnabled)
@@ -142,9 +142,7 @@
}
@Test
- fun `filters modes for summary supportedModes`(
- @TestParameter testCase: SupportedModesTestCase
- ) {
+ fun testFiltersModes_supportedModes(@TestParameter testCase: SupportedModesTestCase) {
val summary = createSummary(testCase.supportedModesVoteEnabled)
summary.supportedModeIds = testCase.summarySupportedModes
@@ -154,7 +152,7 @@
}
@Test
- fun `summary invalid if has requestedRefreshRate less than minRenederRate`() {
+ fun testInvalidSummary_requestedRefreshRateLessThanMinRenderRate() {
val summary = createSummary()
summary.requestedRefreshRates = setOf(30f, 90f)
summary.minRenderFrameRate = 60f
@@ -166,7 +164,7 @@
}
@Test
- fun `summary invalid if has requestedRefreshRate more than maxRenderFrameRate`() {
+ fun testInvalidSummary_requestedRefreshRateMoreThanMaxRenderRate() {
val summary = createSummary()
summary.requestedRefreshRates = setOf(60f, 240f)
summary.minRenderFrameRate = 60f
@@ -178,7 +176,7 @@
}
@Test
- fun `summary valid if all requestedRefreshRates inside render rate limits`() {
+ fun testValidSummary_requestedRefreshRatesWithingRenderRateLimits() {
val summary = createSummary()
summary.requestedRefreshRates = setOf(60f, 90f)
summary.minRenderFrameRate = 60f
diff --git a/services/tests/dreamservicetests/Android.bp b/services/tests/dreamservicetests/Android.bp
index 6369d45..1f0e975 100644
--- a/services/tests/dreamservicetests/Android.bp
+++ b/services/tests/dreamservicetests/Android.bp
@@ -42,3 +42,13 @@
enabled: false,
},
}
+
+test_module_config {
+ name: "DreamServiceTests_server_dreams",
+ base: "DreamServiceTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.dreams"],
+}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 3c714a4..6fc80b8 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -138,8 +138,6 @@
auto_gen_config: true,
}
-FLAKY = ["androidx.test.filters.FlakyTest"]
-
test_module_config {
name: "FrameworksMockingServicesTests_blob",
base: "FrameworksMockingServicesTests",
@@ -152,7 +150,6 @@
base: "FrameworksMockingServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.DeviceIdleControllerTest"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -161,7 +158,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.AppStateTrackerTest"],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -177,7 +173,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.alarm"],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -185,7 +180,7 @@
base: "FrameworksMockingServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.job"],
- exclude_annotations: FLAKY + ["androidx.test.filters.LargeTest"],
+ exclude_annotations: ["androidx.test.filters.LargeTest"],
}
test_module_config {
@@ -200,7 +195,6 @@
base: "FrameworksMockingServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.tare"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -215,7 +209,6 @@
base: "FrameworksMockingServicesTests",
test_suites: ["device-tests"],
include_filters: ["android.service.games"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -245,7 +238,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.am."],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -265,7 +257,6 @@
test_suites: ["device-tests"],
// Matches appop too
include_filters: ["com.android.server.app"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -301,7 +292,6 @@
base: "FrameworksMockingServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.pm"],
- exclude_annotations: FLAKY + ["org.junit.Ignore"],
}
test_module_config {
@@ -309,7 +299,6 @@
base: "FrameworksMockingServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.power"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -324,7 +313,6 @@
base: "FrameworksMockingServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.trust"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -333,3 +321,73 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.utils"],
}
+
+test_module_config {
+ name: "FrameworksMockingServicesTests_android_server",
+ base: "FrameworksMockingServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server"],
+}
+
+test_module_config {
+ name: "FrameworksMockingServicesTests_server_job",
+ base: "FrameworksMockingServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.job"],
+}
+
+test_module_config {
+ name: "FrameworksMockingServicesTests_server_tare",
+ base: "FrameworksMockingServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.tare"],
+}
+
+test_module_config {
+ name: "FrameworksMockingServicesTests_server_backup",
+ base: "FrameworksMockingServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.backup"],
+}
+
+test_module_config {
+ name: "FrameworksMockingServicesTests_server_rescuepartytest",
+ base: "FrameworksMockingServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.RescuePartyTest"],
+}
+
+test_module_config {
+ name: "FrameworksMockingServicesTests_server_power",
+ base: "FrameworksMockingServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.power"],
+}
+
+test_module_config {
+ name: "FrameworksMockingServicesTests_server_trust",
+ base: "FrameworksMockingServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.trust"],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 51aa528..5ec5302 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -52,6 +52,7 @@
import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ;
import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ;
import static com.android.server.am.ProcessList.HOME_APP_ADJ;
+import static com.android.server.am.ProcessList.INVALID_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ;
@@ -90,7 +91,6 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.ApplicationExitInfo;
-import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.content.ComponentName;
import android.content.Context;
@@ -107,6 +107,7 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.server.LocalServices;
import com.android.server.wm.ActivityServiceConnectionsHolder;
@@ -164,6 +165,10 @@
private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
+ ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ private static int sFirstUiCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
+ private static int sFirstNonUiCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 20;
+ private static int sUiTierSize = 5;
+
private Context mContext;
private PackageManagerInternal mPackageManagerInternal;
private ActivityManagerService mService;
@@ -232,9 +237,6 @@
mInjector);
mService.mOomAdjuster.mAdjSeq = 10000;
mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
- if (mService.mConstants.USE_TIERED_CACHED_ADJ) {
- sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
- }
mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
}
@@ -244,6 +246,7 @@
mService.mOomAdjuster.resetInternal();
mService.mOomAdjuster.mActiveUids.clear();
LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ mInjector.reset();
}
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
@@ -435,6 +438,28 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoOne_TopSleepingReceivingBroadcast() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(app).when(mService).getTopApp();
+ updateOomAdj(app);
+
+ assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ,
+ SCHED_GROUP_BACKGROUND);
+ assertTrue(app.mState.hasForegroundActivities());
+
+ doReturn(true).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class),
+ any(int[].class));
+ updateOomAdj(app);
+
+ assertProcStates(app, PROCESS_STATE_RECEIVER, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND);
+ assertTrue(app.mState.hasForegroundActivities());
+
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoOne_ExecutingService() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
@@ -473,7 +498,8 @@
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- final int expectedAdj = sFirstCachedAdj;
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstUiCachedAdj : sFirstCachedAdj;
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj,
SCHED_GROUP_BACKGROUND);
}
@@ -701,7 +727,9 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertEquals(sFirstCachedAdj, app.mState.getSetAdj());
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstUiCachedAdj : sFirstCachedAdj;
+ assertEquals(expectedAdj, app.mState.getSetAdj());
// Follow up should not have been called again.
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
followUpTimeCaptor.capture());
@@ -836,7 +864,9 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, CACHED_APP_MIN_ADJ,
+ int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstUiCachedAdj : CACHED_APP_MIN_ADJ;
+ assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, expectedAdj,
SCHED_GROUP_BACKGROUND, "previous-expired");
// Follow up should not have been called again.
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -877,9 +907,15 @@
for (int i = 0; i < numberOfApps; i++) {
final int mruIndex = numberOfApps - i - 1;
- int expectedAdj = CACHED_APP_MIN_ADJ + (mruIndex * 2 * CACHED_APP_IMPORTANCE_LEVELS);
- if (expectedAdj > CACHED_APP_MAX_ADJ) {
- expectedAdj = CACHED_APP_MAX_ADJ;
+ int expectedAdj;
+ if (mService.mConstants.USE_TIERED_CACHED_ADJ) {
+ expectedAdj = (i < numberOfApps - sUiTierSize)
+ ? sFirstNonUiCachedAdj : sFirstUiCachedAdj + mruIndex;
+ } else {
+ expectedAdj = CACHED_APP_MIN_ADJ + (mruIndex * 2 * CACHED_APP_IMPORTANCE_LEVELS);
+ if (expectedAdj > CACHED_APP_MAX_ADJ) {
+ expectedAdj = CACHED_APP_MAX_ADJ;
+ }
}
assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, expectedAdj,
SCHED_GROUP_BACKGROUND, "previous-expired");
@@ -1003,7 +1039,9 @@
updateOomAdj(client, app);
doReturn(null).when(mService).getTopApp();
- assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1053,7 +1091,9 @@
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1164,6 +1204,25 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoOne_BoundFgService_Sleeping() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
+ client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+ updateOomAdj(client, app);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+
+ assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
+ SCHED_GROUP_RESTRICTED);
+ assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ,
+ SCHED_GROUP_DEFAULT);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoOne_Service_BoundNotForeground() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
@@ -1469,7 +1528,9 @@
bindProvider(app, app, null, null, false);
updateOomAdj(app);
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1484,7 +1545,9 @@
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client);
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1592,7 +1655,9 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
"cch-empty");
// Follow up should not have been called again.
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -2623,12 +2688,11 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
final int userOwner = 0;
final int userOther = 1;
- final int cachedAdj1 = mService.mConstants.USE_TIERED_CACHED_ADJ
- ? CACHED_APP_MIN_ADJ + 10
- : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
- final int cachedAdj2 = mService.mConstants.USE_TIERED_CACHED_ADJ
- ? CACHED_APP_MIN_ADJ + 10
- : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+
+ // cachedAdj1 and cachedAdj2 will be read if USE_TIERED_CACHED_ADJ is disabled. Otherwise,
+ // sFirstUiCachedAdj and sFirstNonUiCachedAdj are used instead.
+ final int cachedAdj1 = CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
doReturn(userOwner).when(mService.mUserController).getCurrentUserId();
final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP();
@@ -2669,8 +2733,12 @@
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj();
- assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
- assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj2, "cch-started-services");
+ assertProcStates(app, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstUiCachedAdj : cachedAdj1,
+ SCHED_GROUP_BACKGROUND, "cch-started-ui-services", true);
+ assertProcStates(app2, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj2,
+ SCHED_GROUP_BACKGROUND, "cch-started-services", true);
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
app.mState.setAdjType(null);
@@ -2678,7 +2746,8 @@
app.mState.setHasShownUi(false);
updateOomAdj();
- assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+ assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+ "started-services", false);
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
app.mState.setAdjType(null);
@@ -2686,7 +2755,9 @@
s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
updateOomAdj();
- assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ assertProcStates(app, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+ SCHED_GROUP_BACKGROUND, "cch-started-services", true);
app.mServices.stopService(s);
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
@@ -2704,8 +2775,11 @@
app.mServices.startService(s);
updateOomAdj();
- assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
- assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+ "started-services", false);
+ assertProcStates(app2, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+ SCHED_GROUP_BACKGROUND, "cch-started-services", true);
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
app.mState.setAdjType(null);
@@ -2714,15 +2788,45 @@
s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
updateOomAdj();
- assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
- assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+ "started-services", false);
+ assertProcStates(app2, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+ SCHED_GROUP_BACKGROUND, "cch-started-services", true);
doReturn(userOther).when(mService.mUserController).getCurrentUserId();
mService.mOomAdjuster.handleUserSwitchedLocked();
updateOomAdj();
- assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
- assertProcStates(app2, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+ assertProcStates(app, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+ SCHED_GROUP_BACKGROUND, "cch-started-services", true);
+ assertProcStates(app2, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+ "started-services", false);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testUpdateOomAdj_DoOne_AboveClient() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ ProcessRecord service = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, true));
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(app).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ updateOomAdj(app);
+
+ assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
+
+ // Simulate binding to a service in the same process using BIND_ABOVE_CLIENT and
+ // verify that its OOM adjustment level is unaffected.
+ bindService(service, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
+ app.mServices.updateHasAboveClientLocked();
+ assertTrue(app.mServices.hasAboveClient());
+
+ updateOomAdj(app);
+ assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -2998,7 +3102,9 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND,
"cch-started-services");
// Follow up should not have been called again.
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -3031,14 +3137,16 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
"cch-empty");
verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
"cch-empty");
}
@@ -3121,8 +3229,10 @@
private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
int expectedSchedGroup) {
final ProcessStateRecord state = app.mState;
+ final int pid = app.getPid();
assertEquals(expectedProcState, state.getSetProcState());
assertEquals(expectedAdj, state.getSetAdj());
+ assertEquals(expectedAdj, mInjector.mLastSetOomAdj.get(pid, INVALID_ADJ));
assertEquals(expectedSchedGroup, state.getSetSchedGroup());
// Below BFGS should never have BFSL.
@@ -3136,41 +3246,19 @@
}
@SuppressWarnings("GuardedBy")
- private void assertProcStates(ProcessRecord app, boolean expectedCached,
- int expectedProcState, int expectedAdj, String expectedAdjType) {
- final ProcessStateRecord state = app.mState;
- assertEquals(expectedCached, state.isCached());
- assertEquals(expectedProcState, state.getSetProcState());
- assertEquals(expectedAdj, state.getSetAdj());
- assertEquals(expectedAdjType, state.getAdjType());
-
- // Below BFGS should never have BFSL.
- if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
- assertNoBfsl(app);
- }
- // Above FGS should always have BFSL.
- if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) {
- assertBfsl(app);
- }
- }
-
- @SuppressWarnings("GuardedBy")
private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
int expectedSchedGroup, String expectedAdjType) {
+ assertProcStates(app, expectedProcState, expectedAdj, expectedSchedGroup);
final ProcessStateRecord state = app.mState;
assertEquals(expectedAdjType, state.getAdjType());
- assertEquals(expectedProcState, state.getSetProcState());
- assertEquals(expectedAdj, state.getSetAdj());
- assertEquals(expectedSchedGroup, state.getSetSchedGroup());
+ }
- // Below BFGS should never have BFSL.
- if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
- assertNoBfsl(app);
- }
- // Above FGS should always have BFSL.
- if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) {
- assertBfsl(app);
- }
+ @SuppressWarnings("GuardedBy")
+ private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
+ int expectedSchedGroup, String expectedAdjType, boolean expectedCached) {
+ assertProcStates(app, expectedProcState, expectedAdj, expectedSchedGroup, expectedAdjType);
+ final ProcessStateRecord state = app.mState;
+ assertEquals(expectedCached, state.isCached());
}
private class ProcessRecordBuilder {
@@ -3255,6 +3343,7 @@
eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt());
ProcessRecord app = new ProcessRecord(mService, ai, mProcessName, mUid,
mSdkSandboxClientAppPackage, -1, null);
+ app.setPid(mPid);
final ProcessStateRecord state = app.mState;
final ProcessServiceRecord services = app.mServices;
final ProcessReceiverRecord receivers = app.mReceivers;
@@ -3319,6 +3408,13 @@
static class OomAdjusterInjector extends OomAdjuster.Injector {
// Jump ahead in time by this offset amount.
long mTimeOffsetMillis = 0;
+ private SparseIntArray mLastSetOomAdj = new SparseIntArray();
+
+ void reset() {
+ mTimeOffsetMillis = 0;
+ mLastSetOomAdj.clear();
+ }
+
void jumpUptimeAheadTo(long uptimeMillis) {
final long jumpMs = uptimeMillis - getUptimeMillis();
@@ -3335,5 +3431,25 @@
long getElapsedRealtimeMillis() {
return SystemClock.elapsedRealtime() + mTimeOffsetMillis;
}
+
+ @Override
+ void batchSetOomAdj(ArrayList<ProcessRecord> procsToOomAdj) {
+ for (ProcessRecord proc : procsToOomAdj) {
+ final int pid = proc.getPid();
+ if (pid <= 0) continue;
+ mLastSetOomAdj.put(pid, proc.mState.getCurAdj());
+ }
+ }
+
+ @Override
+ void setOomAdj(int pid, int uid, int adj) {
+ if (pid <= 0) return;
+ mLastSetOomAdj.put(pid, adj);
+ }
+
+ @Override
+ void setThreadPriority(int tid, int priority) {
+ // do nothing
+ }
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java
new file mode 100644
index 0000000..6f38fca
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.server.crashrecovery;
+
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.quality.Strictness.LENIENT;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+
+/**
+ * Test CrashRecovery Utils.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CrashRecoveryUtilsTest {
+
+ private MockitoSession mStaticMockSession;
+ private final String mLogMsg = "Logging from test";
+ private final String mCrashrecoveryEventTag = "CrashRecovery Events: ";
+ private File mCacheDir;
+
+ @Before
+ public void setup() throws IOException {
+ Context context = ApplicationProvider.getApplicationContext();
+ mCacheDir = context.getCacheDir();
+ mStaticMockSession = ExtendedMockito.mockitoSession()
+ .spyStatic(Environment.class)
+ .strictness(LENIENT)
+ .startMocking();
+ ExtendedMockito.doReturn(mCacheDir).when(() -> Environment.getDataDirectory());
+
+ createCrashRecoveryEventsTempDir();
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ mStaticMockSession.finishMocking();
+ deleteCrashRecoveryEventsTempFile();
+ }
+
+ @Test
+ public void testCrashRecoveryUtils() {
+ testLogCrashRecoveryEvent();
+ testDumpCrashRecoveryEvents();
+ }
+
+ @Test
+ public void testDumpCrashRecoveryEventsWithoutAnyLogs() {
+ assertThat(getCrashRecoveryEventsTempFile().exists()).isFalse();
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter ipw = new IndentingPrintWriter(sw, " ");
+ CrashRecoveryUtils.dumpCrashRecoveryEvents(ipw);
+ ipw.close();
+
+ String dump = sw.getBuffer().toString();
+ assertThat(dump).contains(mCrashrecoveryEventTag);
+ assertThat(dump).doesNotContain(mLogMsg);
+ }
+
+ private void testLogCrashRecoveryEvent() {
+ assertThat(getCrashRecoveryEventsTempFile().exists()).isFalse();
+ CrashRecoveryUtils.logCrashRecoveryEvent(Log.WARN, mLogMsg);
+
+ assertThat(getCrashRecoveryEventsTempFile().exists()).isTrue();
+ String fileContent = null;
+ try {
+ File file = getCrashRecoveryEventsTempFile();
+ FileInputStream fis = new FileInputStream(file);
+ byte[] data = new byte[(int) file.length()];
+ fis.read(data);
+ fis.close();
+ fileContent = new String(data, StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ fail("Unable to read the events file");
+ }
+ assertThat(fileContent).contains(mLogMsg);
+ }
+
+ private void testDumpCrashRecoveryEvents() {
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter ipw = new IndentingPrintWriter(sw, " ");
+ CrashRecoveryUtils.dumpCrashRecoveryEvents(ipw);
+ ipw.close();
+
+ String dump = sw.getBuffer().toString();
+ assertThat(dump).contains(mCrashrecoveryEventTag);
+ assertThat(dump).contains(mLogMsg);
+ }
+
+ private void createCrashRecoveryEventsTempDir() throws IOException {
+ Files.deleteIfExists(getCrashRecoveryEventsTempFile().toPath());
+ File mMockDirectory = new File(mCacheDir, "system");
+ if (!mMockDirectory.exists()) {
+ assertThat(mMockDirectory.mkdir()).isTrue();
+ }
+ }
+
+ private void deleteCrashRecoveryEventsTempFile() throws IOException {
+ Files.deleteIfExists(getCrashRecoveryEventsTempFile().toPath());
+ }
+
+ private File getCrashRecoveryEventsTempFile() {
+ File systemTempDir = new File(mCacheDir, "system");
+ return new File(systemTempDir, "crashrecovery-events.txt");
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java
new file mode 100644
index 0000000..c8e4f89
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.server.job;
+
+import static android.app.job.Flags.FLAG_CLEANUP_EMPTY_JOBS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+
+import android.app.job.IJobCallback;
+import android.app.job.JobParameters;
+import android.net.Uri;
+import android.os.Parcel;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+public class JobParametersTest {
+ private static final String TAG = JobParametersTest.class.getSimpleName();
+ private static final int TEST_JOB_ID_1 = 123;
+ private static final String TEST_NAMESPACE = "TEST_NAMESPACE";
+ private static final String TEST_DEBUG_STOP_REASON = "TEST_DEBUG_STOP_REASON";
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private MockitoSession mMockingSession;
+ @Mock private Parcel mMockParcel;
+ @Mock private IJobCallback.Stub mMockJobCallbackStub;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockingSession =
+ mockitoSession().initMocks(this).strictness(Strictness.LENIENT).startMocking();
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+
+ when(mMockParcel.readInt())
+ .thenReturn(TEST_JOB_ID_1) // Job ID
+ .thenReturn(0) // No clip data
+ .thenReturn(0) // No deadline expired
+ .thenReturn(0) // No network
+ .thenReturn(0) // No stop reason
+ .thenReturn(0); // Internal stop reason
+ when(mMockParcel.readString())
+ .thenReturn(TEST_NAMESPACE) // Job namespace
+ .thenReturn(TEST_DEBUG_STOP_REASON); // Debug stop reason
+ when(mMockParcel.readPersistableBundle()).thenReturn(null);
+ when(mMockParcel.readBundle()).thenReturn(null);
+ when(mMockParcel.readStrongBinder()).thenReturn(mMockJobCallbackStub);
+ when(mMockParcel.readBoolean())
+ .thenReturn(false) // expedited
+ .thenReturn(false); // user initiated
+ when(mMockParcel.createTypedArray(any())).thenReturn(new Uri[0]);
+ when(mMockParcel.createStringArray()).thenReturn(new String[0]);
+ }
+
+ /**
+ * Test to verify that the JobParameters created using Non-Parcelable constructor has not
+ * cleaner attached
+ */
+ @Test
+ public void testJobParametersNonParcelableConstructor_noCleaner() {
+ JobParameters jobParameters =
+ new JobParameters(
+ null,
+ TEST_NAMESPACE,
+ TEST_JOB_ID_1,
+ null,
+ null,
+ null,
+ 0,
+ false,
+ false,
+ false,
+ null,
+ null,
+ null);
+
+ // Verify that cleaner is not registered
+ assertThat(jobParameters.getCleanable()).isNull();
+ assertThat(jobParameters.getJobCleanupCallback()).isNull();
+ }
+
+ /**
+ * Test to verify that the JobParameters created using Parcelable constructor has not cleaner
+ * attached
+ */
+ @Test
+ public void testJobParametersParcelableConstructor_noCleaner() {
+ JobParameters jobParameters = JobParameters.CREATOR.createFromParcel(mMockParcel);
+
+ // Verify that cleaner is not registered
+ assertThat(jobParameters.getCleanable()).isNull();
+ assertThat(jobParameters.getJobCleanupCallback()).isNull();
+ }
+
+ /** Test to verify that the JobParameters Cleaner is disabled */
+ @RequiresFlagsEnabled(FLAG_CLEANUP_EMPTY_JOBS)
+ @Test
+ public void testCleanerWithLeakedJobCleanerDisabled_flagCleanupEmptyJobsEnabled() {
+ // Inject real JobCallbackCleanup
+ JobParameters jobParameters = JobParameters.CREATOR.createFromParcel(mMockParcel);
+
+ // Enable the cleaner
+ jobParameters.enableCleaner();
+
+ // Verify the cleaner is enabled
+ assertThat(jobParameters.getCleanable()).isNotNull();
+ assertThat(jobParameters.getJobCleanupCallback()).isNotNull();
+ assertThat(jobParameters.getJobCleanupCallback().isCleanerEnabled()).isTrue();
+
+ // Disable the cleaner
+ jobParameters.disableCleaner();
+
+ // Verify the cleaner is disabled
+ assertThat(jobParameters.getCleanable()).isNull();
+ assertThat(jobParameters.getJobCleanupCallback()).isNull();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
index 0ae304c..677ecf4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
@@ -54,3 +54,13 @@
"automotive-tests",
],
}
+
+test_module_config {
+ name: "RollbackPackageHealthObserverTests_server_rollback",
+ base: "RollbackPackageHealthObserverTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.rollback"],
+}
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 7d04470..b2ca991 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -631,7 +631,7 @@
CountDownLatch stopLatch2 = new CountDownLatch(1);
// negative value used for test only to avoid conflicting with any real thread that exists
int isoProc1 = -100;
- int isoProc2 = 9999;
+ int isoProc2 = 99999999;
when(mAmInternalMock.getIsolatedProcesses(eq(UID))).thenReturn(List.of(0));
int[] tids2 = createThreads(threadCount, stopLatch2);
int[] tids2WithIsolated = Arrays.copyOf(tids2, tids2.length + 2);
@@ -658,7 +658,7 @@
verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
// the new TIDs pending list should be updated
- assertArrayEquals(session2.getTidsInternal(), expectedTids2);
+ assertArrayEquals(expectedTids2, session2.getTidsInternal());
reset(mNativeWrapperMock);
// this should resume and update the threads so those never-existed invalid isolated
@@ -713,8 +713,8 @@
// in background, set threads for session 1 then it should not be force paused next time
session1.setThreads(SESSION_TIDS_A);
// the new TIDs pending list should be updated
- assertArrayEquals(session1.getTidsInternal(), SESSION_TIDS_A);
- assertArrayEquals(session2.getTidsInternal(), expectedTids2);
+ assertArrayEquals(SESSION_TIDS_A, session1.getTidsInternal());
+ assertArrayEquals(expectedTids2, session2.getTidsInternal());
verifyAllHintsEnabled(session1, false);
verifyAllHintsEnabled(session2, false);
reset(mNativeWrapperMock);
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 2beb153..f03043e 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -44,3 +44,13 @@
enabled: false,
},
}
+
+test_module_config {
+ name: "PowerServiceTests_server_power",
+ base: "PowerServiceTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.power"],
+}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index 1c4db6a..c1d7c7b 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.PowerManager;
+import android.os.Process;
import org.junit.Before;
import org.junit.Test;
@@ -54,6 +55,8 @@
when(mPackageManager.getPackagesForUid(101)).thenReturn(new String[]{ "some.package1" });
when(mPackageManager.getPackagesForUid(102)).thenReturn(new String[]{ "some.package2" });
+ when(mPackageManager.getPackagesForUid(Process.SYSTEM_UID))
+ .thenReturn(new String[]{ "some.package3" });
}
@Test
@@ -70,14 +73,20 @@
log.onWakeLockAcquired("TagFull", 102,
PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, -1);
+ when(injectorSpy.currentTimeMillis()).thenReturn(1250L);
+ log.onWakeLockAcquired("TagSystem", 1000,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, -1);
+
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+ "(partial,on-after-release)\n"
+ " 01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull "
+ "(full,acq-causes-wake)\n"
+ + " 01-01 00:00:01.250 - 1000 (" + WakeLockLog.SYSTEM_PACKAGE_NAME + ")"
+ + " - ACQ TagSystem (full,acq-causes-wake)\n"
+ " -\n"
- + " Events: 2, Time-Resets: 0\n"
- + " Buffer, Bytes used: 6\n",
+ + " Events: 3, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 9\n",
dumpLog(log, false));
}
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 129d683..d6ca10a 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -71,9 +71,31 @@
],
srcs: [
"src/com/android/server/power/stats/*.java",
+ "src/com/android/server/power/stats/format/*.java",
+ "src/com/android/server/power/stats/processor/*.java",
],
java_resources: [
"res/xml/power_profile*.xml",
],
auto_gen_config: true,
}
+
+test_module_config {
+ name: "PowerStatsTests_stats_bstatscputimesvalidationtest",
+ base: "PowerStatsTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.power.stats.BstatsCpuTimesValidationTest"],
+}
+
+test_module_config {
+ name: "PowerStatsTests_power_stats",
+ base: "PowerStatsTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.power.stats"],
+}
diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING
index fb24361..1e8d2de 100644
--- a/services/tests/powerstatstests/TEST_MAPPING
+++ b/services/tests/powerstatstests/TEST_MAPPING
@@ -14,8 +14,7 @@
"name": "PowerStatsTestsRavenwood",
"host": true,
"options": [
- {"include-filter": "com.android.server.power.stats"},
- {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"}
+ {"include-filter": "com.android.server.power.stats"}
]
}
],
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index a1101cd..1d20538 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -117,7 +117,7 @@
private PowerStatsStore mPowerStatsStore;
private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
@Mock
- private PowerStatsExporter mPowerStatsExporter;
+ private PowerAttributor mPowerAttributor;
@Before
public void setUp() throws IOException {
@@ -149,9 +149,8 @@
} else {
context = InstrumentationRegistry.getContext();
}
- mPowerStatsStore = new PowerStatsStore(systemDir, mHandler,
- new AggregatedPowerStatsConfig());
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerStatsExporter,
+ mPowerStatsStore = new PowerStatsStore(systemDir, mHandler);
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerAttributor,
mPowerProfile, mBatteryStatsImpl.getCpuScalingPolicies(), mPowerStatsStore,
mMockClock);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 17c7efa..fde84e9 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -94,8 +94,9 @@
public void test_getBatteryUsageStats() {
BatteryStatsImpl batteryStats = prepareBatteryStats();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
- mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+ mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT);
@@ -130,8 +131,9 @@
public void test_selectPowerComponents() {
BatteryStatsImpl batteryStats = prepareBatteryStats();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
- mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+ mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -235,8 +237,9 @@
batteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000);
}
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
- mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+ mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -323,8 +326,9 @@
}
}
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
- mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+ mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -408,12 +412,12 @@
PowerStatsStore powerStatsStore = new PowerStatsStore(
new File(mStatsRule.getHistoryDir(), "powerstatsstore"),
- mStatsRule.getHandler(), null);
+ mStatsRule.getHandler());
powerStatsStore.reset();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
- mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore,
- mMockClock);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+ mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies(), powerStatsStore, mMockClock);
batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore);
synchronized (batteryStats) {
@@ -522,8 +526,9 @@
batteryStats.updateCustomEnergyConsumerStatsLocked(1, 200_000_000, uidEnergies);
}
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
- mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+ mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
PowerStatsStore powerStatsStore = mock(PowerStatsStore.class);
doAnswer(invocation -> {
@@ -584,9 +589,9 @@
when(powerStatsStore.loadPowerStatsSpan(1, BatteryUsageStatsSection.TYPE))
.thenReturn(span1);
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
- mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore,
- mMockClock);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+ mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies(), powerStatsStore, mMockClock);
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
.aggregateSnapshots(0, 3000)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
index 02c7b74..e392c5d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
@@ -27,6 +27,7 @@
import android.bluetooth.UidTraffic;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.Handler;
@@ -37,6 +38,7 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -48,7 +50,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
-import java.util.function.IntSupplier;
public class BluetoothPowerStatsCollectorTest {
private static final int APP_UID1 = 42;
@@ -132,11 +133,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> 3500;
- }
-
- @Override
public BluetoothPowerStatsCollector.BluetoothStatsRetriever
getBluetoothStatsRetriever() {
return mBluetoothStatsRetriever;
@@ -232,6 +228,7 @@
BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
collector.setEnabled(true);
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
.thenReturn(new int[]{777});
@@ -242,8 +239,7 @@
mUidScanTimes.put(APP_UID1, 100);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
- .thenReturn(new long[]{10000});
+ mockConsumedEnergy(777, 10000);
// Establish a baseline
collector.collectStats();
@@ -258,13 +254,19 @@
mUidScanTimes.put(APP_UID2, 300);
mUidScanTimes.put(ISOLATED_UID, 400);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
- .thenReturn(new long[]{64321});
+ mockConsumedEnergy(777, 64321);
mStatsRule.setTime(20000, 20000);
return collector.collectStats();
}
+ private void mockConsumedEnergy(int consumerId, long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{consumerId})))
+ .thenReturn(new EnergyConsumerResult[]{ecr});
+ }
+
private BluetoothActivityEnergyInfo mockBluetoothActivityEnergyInfo(long timestamp,
long rxTimeMs, long txTimeMs, long idleTimeMs, UidTraffic... uidTraffic) {
if (RavenwoodRule.isOnRavenwood()) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index d1105a4..1fea462 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.ConditionVariable;
@@ -42,6 +43,7 @@
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -53,7 +55,6 @@
import java.io.IOException;
import java.io.StringWriter;
-import java.util.function.IntSupplier;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -124,11 +125,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> 3500;
- }
-
- @Override
public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
return 0;
}
@@ -150,7 +146,9 @@
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
when(mMockKernelCpuStatsReader.isSupportedFeature()).thenReturn(true);
- when(mConsumedEnergyRetriever.getEnergyConsumerIds(anyInt())).thenReturn(new int[0]);
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(anyInt()))
+ .thenReturn(new int[0]);
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
mUidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID_2);
}
@@ -228,9 +226,7 @@
assertThat(descriptor.name).isEqualTo("cpu");
assertThat(descriptor.statsArrayLength).isEqualTo(13);
assertThat(descriptor.uidStatsArrayLength).isEqualTo(5);
- CpuPowerStatsLayout layout =
- new CpuPowerStatsLayout();
- layout.fromExtras(descriptor.extras);
+ CpuPowerStatsLayout layout = new CpuPowerStatsLayout(descriptor);
long[] deviceStats = new long[descriptor.statsArrayLength];
layout.setTimeByScalingStep(deviceStats, 2, 42);
@@ -267,8 +263,7 @@
mockEnergyConsumers();
CpuPowerStatsCollector collector = createCollector(8, 0);
- CpuPowerStatsLayout layout = new CpuPowerStatsLayout();
- layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+ CpuPowerStatsLayout layout = new CpuPowerStatsLayout(collector.getPowerStatsDescriptor());
mockKernelCpuStats(new long[]{1111, 2222, 3333},
new SparseArray<>() {{
@@ -338,8 +333,7 @@
mockEnergyConsumers();
CpuPowerStatsCollector collector = createCollector(8, 0);
- CpuPowerStatsLayout layout = new CpuPowerStatsLayout();
- layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+ CpuPowerStatsLayout layout = new CpuPowerStatsLayout(collector.getPowerStatsDescriptor());
mockKernelCpuStats(new long[]{1111, 2222, 3333},
new SparseArray<>() {{
@@ -462,17 +456,24 @@
private void mockEnergyConsumers() {
reset(mConsumedEnergyRetriever);
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER))
.thenReturn(new int[]{1, 2});
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{1, 2})))
- .thenReturn(new long[]{1000, 2000})
- .thenReturn(new long[]{1500, 2700});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{1, 2})))
+ .thenReturn(new EnergyConsumerResult[]{
+ mockEnergyConsumer(1000), mockEnergyConsumer(2000)})
+ .thenReturn(new EnergyConsumerResult[]{
+ mockEnergyConsumer(1500), mockEnergyConsumer(2700)});
+ }
+
+ private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
}
private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) {
- CpuPowerStatsLayout layout =
- new CpuPowerStatsLayout();
- layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+ CpuPowerStatsLayout layout = new CpuPowerStatsLayout(collector.getPowerStatsDescriptor());
return layout.getScalingStepToPowerBracketMap();
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
index ef20946..00b911b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
@@ -33,6 +33,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
@@ -51,6 +52,7 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -63,7 +65,6 @@
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
@@ -139,11 +140,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> 3500;
- }
-
- @Override
public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
return mNetworkStatsSupplier;
}
@@ -178,6 +174,7 @@
return uid;
}
});
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
mBatteryStats = mStatsRule.getBatteryStats();
}
@@ -242,8 +239,7 @@
assertThat(powerStats.durationMs).isEqualTo(100);
PowerStats.Descriptor descriptor = powerStats.descriptor;
- MobileRadioPowerStatsLayout layout =
- new MobileRadioPowerStatsLayout(descriptor);
+ MobileRadioPowerStatsLayout layout = new MobileRadioPowerStatsLayout(descriptor);
assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200);
assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300);
assertThat(layout.getDeviceCallTime(powerStats.stats)).isEqualTo(40000);
@@ -252,7 +248,7 @@
.isEqualTo((64321 - 10000) * 1000 / 3500);
assertThat(powerStats.stateStats.size()).isEqualTo(2);
- long[] state1 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ long[] state1 = powerStats.stateStats.get(MobileRadioPowerStatsLayout.makeStateKey(
BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
ServiceState.FREQUENCY_RANGE_MMWAVE
));
@@ -263,7 +259,7 @@
assertThat(layout.getStateTxTime(state1, 3)).isEqualTo(4000);
assertThat(layout.getStateTxTime(state1, 4)).isEqualTo(5000);
- long[] state2 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ long[] state2 = powerStats.stateStats.get(MobileRadioPowerStatsLayout.makeStateKey(
BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
ServiceState.FREQUENCY_RANGE_LOW
));
@@ -298,15 +294,14 @@
assertThat(powerStats.durationMs).isEqualTo(100);
PowerStats.Descriptor descriptor = powerStats.descriptor;
- MobileRadioPowerStatsLayout layout =
- new MobileRadioPowerStatsLayout(descriptor);
+ MobileRadioPowerStatsLayout layout = new MobileRadioPowerStatsLayout(descriptor);
assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200);
assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300);
assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
.isEqualTo((64321 - 10000) * 1000 / 3500);
assertThat(powerStats.stateStats.size()).isEqualTo(1);
- long[] stateStats = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ long[] stateStats = powerStats.stateStats.get(MobileRadioPowerStatsLayout.makeStateKey(
AccessNetworkConstants.AccessNetworkType.UNKNOWN,
ServiceState.FREQUENCY_RANGE_UNKNOWN
));
@@ -416,8 +411,8 @@
4321, 321, 1234, 23,
4000, 40, 2000, 20);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
- .thenReturn(new long[]{10000});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{777})))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10000)});
when(mCallDurationSupplier.getAsLong()).thenReturn(10000L);
when(mScanDurationSupplier.getAsLong()).thenReturn(20000L);
@@ -439,8 +434,8 @@
5321, 421, 3234, 223,
8000, 80, 4000, 40);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
- .thenReturn(new long[]{64321});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{777})))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(64321)});
when(mCallDurationSupplier.getAsLong()).thenReturn(50000L);
when(mScanDurationSupplier.getAsLong()).thenReturn(80000L);
@@ -448,6 +443,12 @@
return collector.collectStats();
}
+ private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
+ }
+
private void mockModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
int networkType1, int freqRange1, int rxTimeMs1, @NonNull int[] txTimeMs1,
int networkType2, int freqRange2, int rxTimeMs2, @NonNull int[] txTimeMs2) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
index 89d6c1c..a04f721 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
@@ -114,13 +114,15 @@
mockEnergyConsumers(powerStatsInternal);
PowerStatsCollector.ConsumedEnergyRetrieverImpl retriever =
- new PowerStatsCollector.ConsumedEnergyRetrieverImpl(powerStatsInternal);
+ new PowerStatsCollector.ConsumedEnergyRetrieverImpl(powerStatsInternal, ()-> 3500);
int[] energyConsumerIds = retriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER);
assertThat(energyConsumerIds).isEqualTo(new int[]{1, 2});
- long[] energy = retriever.getConsumedEnergyUws(energyConsumerIds);
- assertThat(energy).isEqualTo(new long[]{1000, 2000});
- energy = retriever.getConsumedEnergyUws(energyConsumerIds);
- assertThat(energy).isEqualTo(new long[]{1500, 2700});
+ EnergyConsumerResult[] energy = retriever.getConsumedEnergy(energyConsumerIds);
+ assertThat(energy[0].energyUWs).isEqualTo(1000);
+ assertThat(energy[1].energyUWs).isEqualTo(2000);
+ energy = retriever.getConsumedEnergy(energyConsumerIds);
+ assertThat(energy[0].energyUWs).isEqualTo(1500);
+ assertThat(energy[1].energyUWs).isEqualTo(2700);
}
@SuppressWarnings("unchecked")
@@ -176,4 +178,11 @@
.thenReturn(future1)
.thenReturn(future2);
}
+
+ private EnergyConsumerResult mockEnergyConsumerResult(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
+ }
+
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
index beec661..143d046 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
@@ -18,36 +18,18 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.os.MonotonicClock;
-
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.IOException;
-import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
public class PowerStatsSchedulerTest {
@@ -56,134 +38,10 @@
.setProvideMainThread(true)
.build();
- private PowerStatsStore mPowerStatsStore;
- private Handler mHandler;
- private MockClock mClock = new MockClock();
- private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
- private PowerStatsScheduler mPowerStatsScheduler;
- private PowerStatsAggregator mPowerStatsAggregator;
- private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
- private List<Long> mScheduledAlarms = new ArrayList<>();
- private boolean mPowerStatsCollectionOccurred;
-
- private static final int START_REALTIME = 7654321;
-
- @Before
- @SuppressWarnings("GuardedBy")
- public void setup() throws IOException {
- TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
-
- mClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
- mClock.realtime = START_REALTIME;
-
- HandlerThread bgThread = new HandlerThread("bg thread");
- bgThread.start();
- mHandler = new Handler(bgThread.getLooper());
- mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
- mPowerStatsStore = new PowerStatsStore(
- Files.createTempDirectory("PowerStatsSchedulerTest").toFile(),
- mHandler, mAggregatedPowerStatsConfig);
- mPowerStatsAggregator = mock(PowerStatsAggregator.class);
- mPowerStatsScheduler = new PowerStatsScheduler(
- () -> mPowerStatsCollectionOccurred = true,
- mPowerStatsAggregator, TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1),
- mPowerStatsStore,
- ((triggerAtMillis, tag, onAlarmListener, handler) ->
- mScheduledAlarms.add(triggerAtMillis)),
- mClock, mMonotonicClock, () -> 12345L, mHandler);
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void storeAggregatePowerStats() {
- mPowerStatsStore.reset();
-
- assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
-
- mPowerStatsStore.storeAggregatedPowerStats(
- createAggregatedPowerStats(mMonotonicClock.monotonicTime(), mClock.currentTime,
- 123));
-
- long delayBeforeAggregating = TimeUnit.MINUTES.toMillis(90);
- mClock.realtime += delayBeforeAggregating;
- mClock.currentTime += delayBeforeAggregating;
-
- doAnswer(invocation -> {
- // The first span is longer than 30 min, because the end time is being aligned with
- // the wall clock. Subsequent spans should be precisely 30 minutes.
- long startTime = invocation.getArgument(0);
- long endTime = invocation.getArgument(1);
- Consumer<AggregatedPowerStats> consumer = invocation.getArgument(2);
-
- long startTimeWallClock =
- mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
- long endTimeWallClock =
- mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
-
- assertThat(startTime).isEqualTo(START_REALTIME + 123);
- assertThat(endTime - startTime).isAtLeast(TimeUnit.MINUTES.toMillis(30));
- assertThat(Instant.ofEpochMilli(endTimeWallClock))
- .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
-
- consumer.accept(
- createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
- return null;
- }).doAnswer(invocation -> {
- long startTime = invocation.getArgument(0);
- long endTime = invocation.getArgument(1);
- Consumer<AggregatedPowerStats> consumer = invocation.getArgument(2);
-
- long startTimeWallClock =
- mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
- long endTimeWallClock =
- mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
-
- assertThat(Instant.ofEpochMilli(startTimeWallClock))
- .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
- assertThat(Instant.ofEpochMilli(endTimeWallClock))
- .isEqualTo(Instant.parse("2023-01-02T04:30:00Z"));
-
- consumer.accept(
- createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
- return null;
- }).when(mPowerStatsAggregator).aggregatePowerStats(anyLong(), anyLong(),
- any(Consumer.class));
-
- mPowerStatsScheduler.start(/*enabled*/ true);
- ConditionVariable done = new ConditionVariable();
- mHandler.post(done::open);
- done.block();
-
- assertThat(mPowerStatsCollectionOccurred).isTrue();
- assertThat(mScheduledAlarms).containsExactly(
- START_REALTIME + TimeUnit.MINUTES.toMillis(90) + TimeUnit.HOURS.toMillis(1));
-
- verify(mPowerStatsAggregator, times(2))
- .aggregatePowerStats(anyLong(), anyLong(), any(Consumer.class));
-
- List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
- assertThat(contents).hasSize(3);
- // Skip the first entry, which was placed in the store at the beginning of this test
- PowerStatsSpan.TimeFrame timeFrame1 = contents.get(1).getTimeFrames().get(0);
- PowerStatsSpan.TimeFrame timeFrame2 = contents.get(2).getTimeFrames().get(0);
- assertThat(timeFrame1.startMonotonicTime).isEqualTo(START_REALTIME + 123);
- assertThat(timeFrame2.startMonotonicTime)
- .isEqualTo(timeFrame1.startMonotonicTime + timeFrame1.duration);
- assertThat(Instant.ofEpochMilli(timeFrame2.startTime))
- .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
- assertThat(Duration.ofMillis(timeFrame2.duration)).isEqualTo(Duration.ofMinutes(30));
- }
-
- private AggregatedPowerStats createAggregatedPowerStats(long monotonicTime, long currentTime,
- long duration) {
- AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
- stats.addClockUpdate(monotonicTime, currentTime);
- stats.setDuration(duration);
- return stats;
- }
-
@Test
public void alignToWallClock() {
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+
// Expect the aligned value to be adjusted by 1 min 30 sec - rounded to the next 15 min
assertThat(PowerStatsScheduler.alignToWallClock(123, TimeUnit.MINUTES.toMillis(15),
123 + TimeUnit.HOURS.toMillis(2),
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
index 36d7af5..dc8d920 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
@@ -59,14 +59,7 @@
clearDirectory(mStoreDirectory);
mPowerStatsStore = new PowerStatsStore(mStoreDirectory,
- MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES,
- new TestHandler(),
- (sectionType, parser) -> {
- if (sectionType.equals(TestSection.TYPE)) {
- return TestSection.readXml(parser);
- }
- return null;
- });
+ MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES, new TestHandler());
}
@Test
@@ -144,7 +137,7 @@
}
@Override
- void write(TypedXmlSerializer serializer) throws IOException {
+ public void write(TypedXmlSerializer serializer) throws IOException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mSize; i++) {
sb.append("X");
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
index 817fdcb..8c09d1d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
@@ -32,6 +33,7 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -39,8 +41,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.function.IntSupplier;
-
public class ScreenPowerStatsCollectorTest {
private static final int APP_UID1 = 42;
private static final int APP_UID2 = 24;
@@ -89,11 +89,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> 3500;
- }
-
- @Override
public int getDisplayCount() {
return 2;
}
@@ -115,6 +110,7 @@
return uid;
}
});
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
}
@Test
@@ -125,8 +121,8 @@
// Establish a baseline
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
.thenReturn(new int[]{77});
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
- .thenReturn(new long[]{10_000});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{77}))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10_000)});
doAnswer(inv -> {
ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
@@ -139,8 +135,8 @@
collector.collectStats();
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
- .thenReturn(new long[]{45_000});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{77}))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(45_000)});
when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
.thenReturn(60_000L);
when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
@@ -171,8 +167,7 @@
PowerStats powerStats = collector.collectStats();
- ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout();
- layout.fromExtras(powerStats.descriptor.extras);
+ ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(powerStats.descriptor);
// (45000 - 10000) / 3500
assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
@@ -204,4 +199,10 @@
assertThat(layout.getUidTopActivityDuration(powerStats.uidStats.get(APP_UID2)))
.isEqualTo(10000);
}
+
+ private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
index b13fc53..8b5e6ee 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.net.NetworkStats;
import android.net.wifi.WifiManager;
@@ -47,6 +48,7 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.WifiPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -57,7 +59,6 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class WifiPowerStatsCollectorTest {
@@ -154,11 +155,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> 3500;
- }
-
- @Override
public Supplier<NetworkStats> getWifiNetworkStatsSupplier() {
return mNetworkStatsSupplier;
}
@@ -368,6 +364,7 @@
WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, null);
collector.setEnabled(true);
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
.thenReturn(new int[]{777});
@@ -385,8 +382,8 @@
mockWifiScanTimes(APP_UID2, 3000, 4000);
mockWifiScanTimes(ISOLATED_UID, 5000, 6000);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
- .thenReturn(new long[]{10000});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{777})))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10_000)});
collector.collectStats();
@@ -404,13 +401,19 @@
mockWifiScanTimes(APP_UID2, 3100, 4200);
mockWifiScanTimes(ISOLATED_UID, 5300, 6400);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
- .thenReturn(new long[]{64321});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{777})))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(64_321)});
mStatsRule.setTime(20000, 20000);
return collector.collectStats();
}
+ private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
+ }
+
private void mockWifiActivityInfo(long timestamp, long rxTimeMs, long txTimeMs, int scanTimeMs,
int idleTimeMs) {
int stackState = 0;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
similarity index 93%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
index 04d53de..0e73329 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static com.google.common.truth.Truth.assertThat;
@@ -49,7 +49,8 @@
private static final int COMPONENT_STATE_1 = 1;
private static final int COMPONENT_STATE_2 = 2;
- private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+ private AggregatedPowerStatsConfig
+ mAggregatedPowerStatsConfig;
private PowerStats.Descriptor mPowerComponentDescriptor;
@Before
@@ -67,7 +68,8 @@
mAggregatedPowerStatsConfig.trackCustomPowerComponents(
() -> new PowerStatsProcessor() {
@Override
- void finish(PowerComponentAggregatedPowerStats stats,
+ void finish(
+ PowerComponentAggregatedPowerStats stats,
long timestampMs) {
}
})
@@ -103,8 +105,8 @@
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new ByteArrayInputStream(baos.toByteArray()), "UTF-8");
- AggregatedPowerStats actualStats = AggregatedPowerStats.createFromXml(parser,
- mAggregatedPowerStatsConfig);
+ AggregatedPowerStats actualStats =
+ AggregatedPowerStats.createFromXml(parser, mAggregatedPowerStatsConfig);
verifyAggregatedPowerStats(actualStats);
}
@@ -163,7 +165,8 @@
return stats;
}
- private void verifyAggregatedPowerStats(AggregatedPowerStats stats) {
+ private void verifyAggregatedPowerStats(
+ AggregatedPowerStats stats) {
PowerStats.Descriptor descriptor = stats.getPowerComponentStats(TEST_POWER_COMPONENT)
.getPowerStatsDescriptor();
assertThat(descriptor.powerComponentId).isEqualTo(TEST_POWER_COMPONENT);
@@ -277,7 +280,8 @@
.isEqualTo(new long[]{250, 300});
}
- private static long[] getDeviceStats(AggregatedPowerStats stats, int powerComponentId,
+ private static long[] getDeviceStats(
+ AggregatedPowerStats stats, int powerComponentId,
int... states) {
PowerComponentAggregatedPowerStats powerComponentStats =
stats.getPowerComponentStats(powerComponentId);
@@ -286,7 +290,8 @@
return out;
}
- private static long[] getStateStats(AggregatedPowerStats stats, int key, int... states) {
+ private static long[] getStateStats(
+ AggregatedPowerStats stats, int key, int... states) {
PowerComponentAggregatedPowerStats powerComponentStats =
stats.getPowerComponentStats(TEST_POWER_COMPONENT);
long[] out = new long[powerComponentStats.getPowerStatsDescriptor().stateStatsArrayLength];
@@ -294,7 +299,8 @@
return out;
}
- private static long[] getUidDeviceStats(AggregatedPowerStats stats, int powerComponentId,
+ private static long[] getUidDeviceStats(
+ AggregatedPowerStats stats, int powerComponentId,
int uid, int... states) {
PowerComponentAggregatedPowerStats powerComponentStats =
stats.getPowerComponentStats(powerComponentId);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
similarity index 68%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
index a2a7e00..21e615f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
@@ -35,7 +35,12 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
-import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.ScreenPowerStatsCollector;
+import com.android.server.power.stats.ScreenPowerStatsCollector.ScreenUsageTimeRetriever;
+import com.android.server.power.stats.format.PowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -43,8 +48,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.function.IntSupplier;
-
public class AmbientDisplayPowerStatsProcessorTest {
@Rule(order = 0)
@@ -64,49 +67,45 @@
@Mock
private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
@Mock
- private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
+ private ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
- private final Injector mInjector = new Injector() {
- @Override
- public Handler getHandler() {
- return mStatsRule.getHandler();
- }
+ private final ScreenPowerStatsCollector.Injector mInjector =
+ new ScreenPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
- @Override
- public Clock getClock() {
- return mStatsRule.getMockClock();
- }
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
- @Override
- public PowerStatsUidResolver getUidResolver() {
- return new PowerStatsUidResolver();
- }
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return new PowerStatsUidResolver();
+ }
- @Override
- public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
- return 0;
- }
+ @Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
- @Override
- public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
- return mConsumedEnergyRetriever;
- }
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
- @Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
+ @Override
+ public int getDisplayCount() {
+ return 2;
+ }
- @Override
- public int getDisplayCount() {
- return 2;
- }
-
- @Override
- public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
- return mScreenUsageTimeRetriever;
- }
- };
+ @Override
+ public ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
+ return mScreenUsageTimeRetriever;
+ }
+ };
@Before
public void setup() {
@@ -167,7 +166,8 @@
return stats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY);
}
- private void assertPowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats,
+ private void assertPowerEstimate(
+ PowerComponentAggregatedPowerStats aggregatedStats,
int powerState, int screenState, double expectedPowerEstimate) {
PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
PowerStatsLayout layout = new PowerStatsLayout(descriptor);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
similarity index 92%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
index 4b40f68..b412ad6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
@@ -14,19 +14,19 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
@@ -40,6 +40,9 @@
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.MockClock;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
import org.junit.Rule;
import org.junit.Test;
@@ -146,7 +149,8 @@
@Test
public void energyConsumerModel() {
- BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+ BinaryStatePowerStatsLayout
+ statsLayout = new BinaryStatePowerStatsLayout();
PersistableBundle extras = new PersistableBundle();
statsLayout.toExtras(extras);
PowerStats.Descriptor descriptor = new PowerStats.Descriptor(POWER_COMPONENT,
@@ -270,9 +274,8 @@
.trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
.setProcessorSupplier(processorSupplier);
- AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
PowerComponentAggregatedPowerStats powerComponentStats =
- aggregatedPowerStats.getPowerComponentStats(POWER_COMPONENT);
+ new AggregatedPowerStats(config).getPowerComponentStats(POWER_COMPONENT);
powerComponentStats.start(0);
powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
similarity index 91%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
index 4a8125f..6dfc220 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
@@ -14,19 +14,19 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
@@ -38,6 +38,7 @@
import android.bluetooth.UidTraffic;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.Handler;
@@ -48,7 +49,12 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.BluetoothPowerStatsCollector;
import com.android.server.power.stats.BluetoothPowerStatsCollector.BluetoothStatsRetriever;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -58,7 +64,6 @@
import java.util.List;
import java.util.concurrent.Executor;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class BluetoothPowerStatsProcessorTest {
@@ -143,11 +148,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
-
- @Override
public BluetoothStatsRetriever getBluetoothStatsRetriever() {
return mBluetoothStatsRetriever;
}
@@ -363,7 +363,8 @@
@Test
public void consumedEnergyModel() {
- // No power monitoring hardware
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
+ // Power monitoring hardware exists
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
.thenReturn(new int[]{BLUETOOTH_ENERGY_CONSUMER_ID});
@@ -378,8 +379,8 @@
mUidScanTimes.put(APP_UID1, 100);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(
- new int[]{BLUETOOTH_ENERGY_CONSUMER_ID})).thenReturn(new long[]{0});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{BLUETOOTH_ENERGY_CONSUMER_ID}))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(0)});
aggregatedStats.start(0);
@@ -404,8 +405,8 @@
// 10 mAh represented as microWattSeconds
long energyUws = 10 * 3600 * VOLTAGE_MV;
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(
- new int[]{BLUETOOTH_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{BLUETOOTH_ENERGY_CONSUMER_ID}))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(energyUws)});
aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
@@ -468,16 +469,15 @@
private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
Supplier<PowerStatsProcessor> processorSupplier) {
- AggregatedPowerStatsConfig.PowerComponent config =
- new AggregatedPowerStatsConfig.PowerComponent(
- BatteryConsumer.POWER_COMPONENT_BLUETOOTH)
- .trackDeviceStates(STATE_POWER, STATE_SCREEN)
- .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
- .setProcessorSupplier(processorSupplier);
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessorSupplier(processorSupplier);
PowerComponentAggregatedPowerStats aggregatedStats =
- new PowerComponentAggregatedPowerStats(
- new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+ new AggregatedPowerStats(config).getPowerComponentStats(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
@@ -491,6 +491,12 @@
return states;
}
+ private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
+ }
+
private BluetoothActivityEnergyInfo mockBluetoothActivityEnergyInfo(long timestamp,
long rxTimeMs, long txTimeMs, long idleTimeMs, UidTraffic... uidTraffic) {
if (RavenwoodRule.isOnRavenwood()) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
similarity index 88%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
index 88a4f5e..0afcbf1 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
@@ -14,23 +14,22 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@@ -46,6 +45,12 @@
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.CameraPowerStatsCollector;
+import com.android.server.power.stats.EnergyConsumerPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -53,7 +58,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class CameraPowerStatsTest {
@@ -103,11 +107,6 @@
public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
return mConsumedEnergyRetriever;
}
-
- @Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
};
private MonotonicClock mMonotonicClock;
@@ -120,8 +119,9 @@
@Test
public void energyConsumerModel() {
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
when(mConsumedEnergyRetriever
- .getEnergyConsumerIds(eq((int) EnergyConsumerType.CAMERA), any()))
+ .getEnergyConsumerIds(eq((int) EnergyConsumerType.CAMERA)))
.thenReturn(new int[]{ENERGY_CONSUMER_ID});
PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
@@ -161,8 +161,7 @@
stats.finish(11_000);
PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
- BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
- statsLayout.fromExtras(descriptor.extras);
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(descriptor);
// Total estimated power = 3,600,000 uC = 1.0 mAh
// of which 3,000,000 is distributed:
@@ -243,7 +242,8 @@
private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
Supplier<PowerStatsProcessor> processorSupplier) {
- AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ AggregatedPowerStatsConfig
+ config = new AggregatedPowerStatsConfig();
config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
.trackDeviceStates(
AggregatedPowerStatsConfig.STATE_POWER,
@@ -254,9 +254,8 @@
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessorSupplier(processorSupplier);
- AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
- PowerComponentAggregatedPowerStats powerComponentStats =
- aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_CAMERA);
+ PowerComponentAggregatedPowerStats powerComponentStats = new AggregatedPowerStats(config)
+ .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_CAMERA);
powerComponentStats.start(0);
powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
similarity index 86%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
index ab2e631..6938615 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
@@ -13,24 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
@@ -42,6 +41,8 @@
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -202,26 +203,18 @@
PowerComponentAggregatedPowerStats {
private final CpuPowerStatsLayout mStatsLayout;
private final PowerStats.Descriptor mDescriptor;
- private HashMap<String, long[]> mDeviceStats = new HashMap<>();
- private HashMap<String, long[]> mUidStats = new HashMap<>();
- private HashSet<Integer> mUids = new HashSet<>();
- private HashMap<String, Double> mExpectedDevicePower = new HashMap<>();
- private HashMap<String, Double> mExpectedUidPower = new HashMap<>();
+ private final HashMap<String, long[]> mDeviceStats = new HashMap<>();
+ private final HashMap<String, long[]> mUidStats = new HashMap<>();
+ private final HashSet<Integer> mUids = new HashSet<>();
+ private final HashMap<String, Double> mExpectedDevicePower = new HashMap<>();
+ private final HashMap<String, Double> mExpectedUidPower = new HashMap<>();
- MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config,
+ MockPowerComponentAggregatedPowerStats(
+ AggregatedPowerStatsConfig.PowerComponent config,
boolean useEnergyConsumers) {
- super(new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
- mStatsLayout = new CpuPowerStatsLayout();
- mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3);
- mStatsLayout.addDeviceSectionCpuTimeByCluster(2);
- mStatsLayout.addDeviceSectionUsageDuration();
- if (useEnergyConsumers) {
- mStatsLayout.addDeviceSectionEnergyConsumers(2);
- }
- mStatsLayout.addDeviceSectionPowerEstimate();
- mStatsLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0, 1, 2});
- mStatsLayout.addUidSectionPowerEstimate();
-
+ super(new AggregatedPowerStats(new AggregatedPowerStatsConfig()), config);
+ mStatsLayout = new CpuPowerStatsLayout(useEnergyConsumers ? 2 : 0, 2,
+ new int[]{0, 1, 2});
PersistableBundle extras = new PersistableBundle();
mStatsLayout.toExtras(extras);
mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
similarity index 90%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
index 8239fdb..42baba7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
@@ -42,6 +42,12 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.CustomEnergyConsumerPowerStatsCollector;
+import com.android.server.power.stats.EnergyConsumerPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -54,7 +60,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
-import java.util.function.IntSupplier;
public class CustomEnergyConsumerPowerStatsTest {
@Rule(order = 0)
@@ -105,11 +110,6 @@
public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
return mConsumedEnergyRetriever;
}
-
- @Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
};
@@ -237,6 +237,7 @@
private List<PowerStats> collectPowerStats(long timestamp, int chargeUc1, int chargeUc2,
EnergyConsumerAttribution... attributions2) throws Exception {
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.OTHER))
.thenReturn(new int[]{ENERGY_CONSUMER_ID1, ENERGY_CONSUMER_ID2});
when(mConsumedEnergyRetriever.getEnergyConsumerName(ENERGY_CONSUMER_ID1))
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
similarity index 93%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
index f22279a..e6207d4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
@@ -14,23 +14,22 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@@ -47,6 +46,12 @@
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.EnergyConsumerPowerStatsCollector;
+import com.android.server.power.stats.GnssPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -54,7 +59,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class GnssPowerStatsTest {
@@ -106,11 +110,6 @@
public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
return mConsumedEnergyRetriever;
}
-
- @Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
};
private MonotonicClock mMonotonicClock;
@@ -127,7 +126,7 @@
public void powerProfileModel() {
// ODPM unsupported
when(mConsumedEnergyRetriever
- .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS), any()))
+ .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS)))
.thenReturn(new int[0]);
PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
@@ -165,8 +164,7 @@
stats.finish(START_TIME + 11_000);
PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
- BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
- statsLayout.fromExtras(descriptor.extras);
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(descriptor);
// scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh
// scr-off GNSS=good: 4500 * 100 = 0.12500 mAh
@@ -218,7 +216,7 @@
public void initialStateGnssOn() {
// ODPM unsupported
when(mConsumedEnergyRetriever
- .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS), any()))
+ .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS)))
.thenReturn(new int[0]);
PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
@@ -245,8 +243,7 @@
stats.finish(START_TIME + 11_000);
PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
- BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
- statsLayout.fromExtras(descriptor.extras);
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(descriptor);
// scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh
// scr-off GNSS=good: 4500 * 100 = 0.12500 mAh
@@ -296,8 +293,9 @@
@Test
public void energyConsumerModel() {
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
when(mConsumedEnergyRetriever
- .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS), any()))
+ .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS)))
.thenReturn(new int[]{ENERGY_CONSUMER_ID});
PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
@@ -339,8 +337,7 @@
stats.finish(START_TIME + 11_000);
PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
- BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
- statsLayout.fromExtras(descriptor.extras);
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(descriptor);
// Total estimated power = 3,600,000 uC = 1.0 mAh
// of which 3,000,000 is distributed:
@@ -442,7 +439,8 @@
private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
Supplier<PowerStatsProcessor> processorSupplier) {
- AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ AggregatedPowerStatsConfig
+ config = new AggregatedPowerStatsConfig();
config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
.trackDeviceStates(
AggregatedPowerStatsConfig.STATE_POWER,
@@ -453,9 +451,8 @@
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessorSupplier(processorSupplier);
- AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
- PowerComponentAggregatedPowerStats powerComponentStats =
- aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS);
+ PowerComponentAggregatedPowerStats powerComponentStats = new AggregatedPowerStats(config)
+ .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS);
powerComponentStats.start(START_TIME);
powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, START_TIME);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
similarity index 87%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
index 89d59a9..80358c5 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.METERED_NO;
@@ -23,12 +23,12 @@
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
@@ -41,6 +41,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
@@ -53,6 +54,11 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.MobileRadioPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -61,7 +67,6 @@
import org.mockito.MockitoAnnotations;
import java.util.List;
-import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
@@ -129,11 +134,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
-
- @Override
public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
return mNetworkStatsSupplier;
}
@@ -172,17 +172,15 @@
mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator");
- AggregatedPowerStatsConfig.PowerComponent config =
- new AggregatedPowerStatsConfig.PowerComponent(
- BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
- .trackDeviceStates(STATE_POWER, STATE_SCREEN)
- .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
- .setProcessorSupplier(() -> new MobileRadioPowerStatsProcessor(
- mStatsRule.getPowerProfile()));
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()));
- PowerComponentAggregatedPowerStats aggregatedStats =
- new PowerComponentAggregatedPowerStats(
- new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+ PowerComponentAggregatedPowerStats aggregatedStats = new AggregatedPowerStats(config)
+ .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
@@ -232,8 +230,7 @@
aggregatedStats.finish(10_000);
MobileRadioPowerStatsLayout statsLayout =
- new MobileRadioPowerStatsLayout(
- aggregatedStats.getPowerStatsDescriptor());
+ new MobileRadioPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
// 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration)
// + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration)
@@ -316,8 +313,7 @@
prepareAggregatedStats_energyConsumerModel();
MobileRadioPowerStatsLayout statsLayout =
- new MobileRadioPowerStatsLayout(
- aggregatedStats.getPowerStatsDescriptor());
+ new MobileRadioPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
// 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh
double totalPower = 0;
@@ -406,23 +402,22 @@
private PowerComponentAggregatedPowerStats prepareAggregatedStats_energyConsumerModel() {
// PowerStats hardware is available
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
.thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.initMeasuredEnergyStatsLocked();
- AggregatedPowerStatsConfig.PowerComponent config =
- new AggregatedPowerStatsConfig.PowerComponent(
- BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
- .trackDeviceStates(STATE_POWER, STATE_SCREEN)
- .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
- .setProcessorSupplier(() -> new MobileRadioPowerStatsProcessor(
- mStatsRule.getPowerProfile()));
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessorSupplier(
+ () -> new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()));
- PowerComponentAggregatedPowerStats aggregatedStats =
- new PowerComponentAggregatedPowerStats(
- new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+ PowerComponentAggregatedPowerStats aggregatedStats = new AggregatedPowerStats(config)
+ .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
@@ -436,9 +431,9 @@
// Initial empty ModemActivityInfo.
mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+ when(mConsumedEnergyRetriever.getConsumedEnergy(
new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{0});
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(0)});
aggregatedStats.start(0);
@@ -467,8 +462,8 @@
mStatsRule.setTime(10_000, 10_000);
long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(
- new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(energyUws)});
when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
@@ -485,6 +480,12 @@
return states;
}
+ private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
+ }
+
private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
doAnswer(invocation -> {
OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java
new file mode 100644
index 0000000..704ee62
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.server.power.stats.processor;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.MonotonicClock;
+import com.android.server.power.stats.MockClock;
+import com.android.server.power.stats.PowerStatsScheduler;
+import com.android.server.power.stats.PowerStatsSpan;
+import com.android.server.power.stats.PowerStatsStore;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+public class MultiStatePowerAttributorTest {
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ private PowerStatsStore mPowerStatsStore;
+ private Handler mHandler;
+ private final MockClock mClock = new MockClock();
+ private final MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
+ private PowerStatsScheduler mPowerStatsScheduler;
+ private PowerStatsAggregator mPowerStatsAggregator;
+ private MultiStatePowerAttributor mPowerAttributor;
+ private final List<Long> mScheduledAlarms = new ArrayList<>();
+ private boolean mPowerStatsCollectionOccurred;
+
+ private static final int START_REALTIME = 7654321;
+
+ @Before
+ public void setup() throws IOException {
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+
+ mClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
+ mClock.realtime = START_REALTIME;
+
+ HandlerThread bgThread = new HandlerThread("bg thread");
+ bgThread.start();
+ mHandler = new Handler(bgThread.getLooper());
+ mPowerStatsStore = new PowerStatsStore(
+ Files.createTempDirectory("MultiStatePowerAttributorTest").toFile(), mHandler);
+ mPowerStatsAggregator = mock(PowerStatsAggregator.class);
+ mPowerAttributor = new MultiStatePowerAttributor(mPowerStatsStore, mPowerStatsAggregator);
+ mPowerStatsScheduler = new PowerStatsScheduler(
+ () -> mPowerStatsCollectionOccurred = true,
+ mock(BatteryStatsHistory.class),
+ mPowerAttributor, TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1),
+ mPowerStatsStore,
+ ((triggerAtMillis, tag, onAlarmListener, handler) ->
+ mScheduledAlarms.add(triggerAtMillis)),
+ mClock, mMonotonicClock, () -> 12345L, mHandler);
+ }
+
+ @Test
+ public void storeAggregatedPowerStats() {
+ mPowerStatsStore.reset();
+
+ assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
+
+ mPowerAttributor.storeAggregatedPowerStats(
+ createAggregatedPowerStats(mMonotonicClock.monotonicTime(), mClock.currentTime,
+ 123));
+
+ long delayBeforeAggregating = TimeUnit.MINUTES.toMillis(90);
+ mClock.realtime += delayBeforeAggregating;
+ mClock.currentTime += delayBeforeAggregating;
+
+ doAnswer(invocation -> {
+ // The first span is longer than 30 min, because the end time is being aligned with
+ // the wall clock. Subsequent spans should be precisely 30 minutes.
+ long startTime = invocation.getArgument(1);
+ long endTime = invocation.getArgument(2);
+ Consumer<AggregatedPowerStats> consumer = invocation.getArgument(3);
+
+ long startTimeWallClock =
+ mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
+ long endTimeWallClock =
+ mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
+
+ assertThat(startTime).isEqualTo(START_REALTIME + 123);
+ assertThat(endTime - startTime).isAtLeast(TimeUnit.MINUTES.toMillis(30));
+ assertThat(Instant.ofEpochMilli(endTimeWallClock))
+ .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+
+ consumer.accept(
+ createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
+ return null;
+ }).doAnswer(invocation -> {
+ long startTime = invocation.getArgument(1);
+ long endTime = invocation.getArgument(2);
+ Consumer<AggregatedPowerStats> consumer = invocation.getArgument(3);
+
+ long startTimeWallClock =
+ mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
+ long endTimeWallClock =
+ mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
+
+ assertThat(Instant.ofEpochMilli(startTimeWallClock))
+ .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+ assertThat(Instant.ofEpochMilli(endTimeWallClock))
+ .isEqualTo(Instant.parse("2023-01-02T04:30:00Z"));
+
+ consumer.accept(
+ createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
+ return null;
+ }).when(mPowerStatsAggregator).aggregatePowerStats(any(BatteryStatsHistory.class),
+ anyLong(), anyLong(), any(Consumer.class));
+
+ mPowerStatsScheduler.start(/*enabled*/ true);
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+
+ assertThat(mPowerStatsCollectionOccurred).isTrue();
+ assertThat(mScheduledAlarms).containsExactly(
+ START_REALTIME + TimeUnit.MINUTES.toMillis(90) + TimeUnit.HOURS.toMillis(1));
+
+ verify(mPowerStatsAggregator, times(2)).aggregatePowerStats(
+ any(BatteryStatsHistory.class), anyLong(), anyLong(), any(Consumer.class));
+
+ List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
+ assertThat(contents).hasSize(3);
+ // Skip the first entry, which was placed in the store at the beginning of this test
+ PowerStatsSpan.TimeFrame timeFrame1 = contents.get(1).getTimeFrames().get(0);
+ PowerStatsSpan.TimeFrame timeFrame2 = contents.get(2).getTimeFrames().get(0);
+ assertThat(timeFrame1.startMonotonicTime).isEqualTo(START_REALTIME + 123);
+ assertThat(timeFrame2.startMonotonicTime)
+ .isEqualTo(timeFrame1.startMonotonicTime + timeFrame1.duration);
+ assertThat(Instant.ofEpochMilli(timeFrame2.startTime))
+ .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+ assertThat(Duration.ofMillis(timeFrame2.duration)).isEqualTo(Duration.ofMinutes(30));
+ }
+
+ private AggregatedPowerStats createAggregatedPowerStats(long monotonicTime, long currentTime,
+ long duration) {
+ AggregatedPowerStats stats = new AggregatedPowerStats(new AggregatedPowerStatsConfig());
+ stats.addClockUpdate(monotonicTime, currentTime);
+ stats.setDuration(duration);
+ return stats;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
similarity index 99%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
index ae258cd3..a232c0c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -198,8 +198,7 @@
new MultiStateStats.States("scr", trackScreenState, "screen-off", "plugged-in"));
}
- private FactorySubject assertThatCpuPerformanceStatsFactory(
- MultiStateStats.Factory factory) {
+ private FactorySubject assertThatCpuPerformanceStatsFactory(MultiStateStats.Factory factory) {
FactorySubject subject = new FactorySubject();
subject.mFactory = factory;
return subject;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
similarity index 84%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
index cb1bcfe..535f2da 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
@@ -41,6 +41,11 @@
import android.telephony.TelephonyManager;
import com.android.internal.os.Clock;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.MobileRadioPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.PowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -48,7 +53,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
@@ -113,11 +117,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
-
- @Override
public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
return mNetworkStatsSupplier;
}
@@ -156,19 +155,17 @@
@Test
public void copyEstimatesFromMobileRadioPowerStats() {
-
- AggregatedPowerStatsConfig aggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
- aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
.trackDeviceStates(STATE_POWER, STATE_SCREEN)
.trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
.setProcessorSupplier(
() -> new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()));
- aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
.setProcessorSupplier(PhoneCallPowerStatsProcessor::new);
- AggregatedPowerStats aggregatedPowerStats =
- new AggregatedPowerStats(aggregatedPowerStatsConfig);
+ AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
PowerComponentAggregatedPowerStats mobileRadioStats =
aggregatedPowerStats.getPowerComponentStats(
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
@@ -208,8 +205,7 @@
aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_PHONE);
stats.finish(10_000);
- PowerStatsLayout statsLayout =
- new PowerStatsLayout(stats.getPowerStatsDescriptor());
+ PowerStatsLayout statsLayout = new PowerStatsLayout(stats.getPowerStatsDescriptor());
long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength];
stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
similarity index 95%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
index 3929137..f312bed 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +32,7 @@
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.MockClock;
import org.junit.Before;
import org.junit.Test;
@@ -53,7 +54,7 @@
private final MockClock mClock = new MockClock();
private final MonotonicClock mMonotonicClock = new MonotonicClock(START_TIME, mClock);
private BatteryStatsHistory mHistory;
- private PowerStatsAggregator mAggregator;
+ private com.android.server.power.stats.processor.PowerStatsAggregator mAggregator;
private int mAggregatedStatsCount;
@Before
@@ -71,7 +72,7 @@
AggregatedPowerStatsConfig.STATE_POWER,
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
- mAggregator = new PowerStatsAggregator(config, mHistory);
+ mAggregator = new PowerStatsAggregator(config);
}
@Test
@@ -119,7 +120,7 @@
powerStats.uidStats.put(TEST_UID, new long[]{4444});
mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
- mAggregator.aggregatePowerStats(0, MonotonicClock.UNDEFINED, stats -> {
+ mAggregator.aggregatePowerStats(mHistory, 0, MonotonicClock.UNDEFINED, stats -> {
assertThat(mAggregatedStatsCount++).isEqualTo(0);
assertThat(stats.getStartTime()).isEqualTo(START_TIME);
@@ -138,7 +139,8 @@
long[] values = new long[1];
- PowerComponentAggregatedPowerStats powerComponentStats = stats.getPowerComponentStats(
+ PowerComponentAggregatedPowerStats
+ powerComponentStats = stats.getPowerComponentStats(
TEST_POWER_COMPONENT);
assertThat(powerComponentStats.getDeviceStats(values, new int[]{
@@ -218,7 +220,7 @@
mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 50, /* plugged */ true);
- mAggregator.aggregatePowerStats(0, MonotonicClock.UNDEFINED, stats -> {
+ mAggregator.aggregatePowerStats(mHistory, 0, MonotonicClock.UNDEFINED, stats -> {
long[] values = new long[1];
PowerComponentAggregatedPowerStats powerComponentStats =
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
similarity index 95%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
index 96203a5..024743d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.mock;
import android.annotation.NonNull;
+import android.content.Context;
import android.os.AggregateBatteryConsumer;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
@@ -37,9 +38,16 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.MockClock;
+import com.android.server.power.stats.PowerStatsStore;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -76,6 +84,7 @@
private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
private PowerStatsStore mPowerStatsStore;
private PowerStatsAggregator mPowerStatsAggregator;
+ private MultiStatePowerAttributor mPowerAttributor;
private BatteryStatsHistory mHistory;
private CpuPowerStatsLayout mCpuStatsArrayLayout;
private PowerStats.Descriptor mPowerStatsDescriptor;
@@ -108,25 +117,23 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
- mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config);
+ mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler());
mHistory = new BatteryStatsHistory(Parcel.obtain(), storeDirectory, 0, 10000,
mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
mMonotonicClock, null, null);
- mPowerStatsAggregator = new PowerStatsAggregator(config, mHistory);
+ mPowerStatsAggregator = new PowerStatsAggregator(config);
- mCpuStatsArrayLayout = new CpuPowerStatsLayout();
- mCpuStatsArrayLayout.addDeviceSectionCpuTimeByScalingStep(1);
- mCpuStatsArrayLayout.addDeviceSectionCpuTimeByCluster(1);
- mCpuStatsArrayLayout.addDeviceSectionUsageDuration();
- mCpuStatsArrayLayout.addDeviceSectionPowerEstimate();
- mCpuStatsArrayLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0});
- mCpuStatsArrayLayout.addUidSectionPowerEstimate();
+ mCpuStatsArrayLayout = new CpuPowerStatsLayout(0, 1, new int[]{0});
PersistableBundle extras = new PersistableBundle();
mCpuStatsArrayLayout.toExtras(extras);
mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
mCpuStatsArrayLayout.getDeviceStatsArrayLength(),
null, 0, mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
+
+ mPowerAttributor = new MultiStatePowerAttributor(mock(Context.class), mPowerStatsStore,
+ mock(PowerProfile.class), mock(CpuScalingPolicies.class),
+ mock(PowerStatsUidResolver.class));
}
@Test
@@ -329,10 +336,12 @@
includeScreenStateData, includesPowerStateData);
}
- private @NonNull BatteryUsageStats exportToBatteryUsageStats(AggregatedPowerStats aps,
+ private @NonNull BatteryUsageStats exportToBatteryUsageStats(
+ AggregatedPowerStats aps,
boolean includeProcessStateData, boolean includeScreenStateData,
boolean includesPowerStateData) {
- PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore,
+ PowerStatsExporter
+ exporter = new PowerStatsExporter(mPowerStatsStore,
mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], false,
@@ -508,8 +517,8 @@
mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 2469);
mHistory.recordPowerStats(3000, 3000, powerStats);
- mPowerStatsAggregator.aggregatePowerStats(0, 3500,
- stats -> mPowerStatsStore.storeAggregatedPowerStats(stats));
+ mPowerStatsAggregator.aggregatePowerStats(mHistory, 0, 3500,
+ stats -> mPowerAttributor.storeAggregatedPowerStats(stats));
mHistory.recordProcessStateChange(4000, 4000, APP_UID1,
BatteryConsumer.PROCESS_STATE_BACKGROUND);
@@ -525,9 +534,8 @@
mEnergyConsumerPowerStatsLayout.setUidConsumedEnergy(customUidStats, 0, 360_000);
mHistory.recordPowerStats(6010, 6010, customPowerStats);
- mPowerStatsAggregator.aggregatePowerStats(3500, 6500, stats -> {
- mPowerStatsStore.storeAggregatedPowerStats(stats);
- });
+ mPowerStatsAggregator.aggregatePowerStats(mHistory, 3500, 6500,
+ stats -> mPowerAttributor.storeAggregatedPowerStats(stats));
mHistory.recordStateStartEvent(7000, 7000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
mHistory.recordProcessStateChange(7000, 7000, APP_UID1,
@@ -548,7 +556,8 @@
recordBatteryHistory();
PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore,
mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
- exporter.exportAggregatedPowerStats(builder, monotonicStartTime, monotonicEndTime);
+ exporter.exportAggregatedPowerStats(builder, mHistory, monotonicStartTime,
+ monotonicEndTime);
}
private void assertAggregatedPowerEstimate(String message, BatteryUsageStats bus, int scope,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsProcessorTest.java
similarity index 94%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsProcessorTest.java
index 02e446a..13e0d9d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsProcessorTest.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
similarity index 83%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
index 94f5662..1852165 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
@@ -14,22 +14,22 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
@@ -40,7 +40,12 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.ScreenPowerStatsCollector;
import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -48,7 +53,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class ScreenPowerStatsProcessorTest {
@@ -106,11 +110,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
-
- @Override
public int getDisplayCount() {
return 2;
}
@@ -124,6 +123,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
}
@Test
@@ -177,8 +177,8 @@
if (energyConsumer) {
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
.thenReturn(new int[]{77});
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
- .thenReturn(new long[]{10_000});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{77}))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10_000)});
} else {
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
.thenReturn(new int[0]);
@@ -200,8 +200,8 @@
if (energyConsumer) {
// 400 mAh represented as microWattSeconds
long energyUws = 400L * 3600 * VOLTAGE_MV;
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
- .thenReturn(new long[]{10_000 + energyUws});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{77}))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10_000 + energyUws)});
}
when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
@@ -243,16 +243,14 @@
private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
Supplier<PowerStatsProcessor> processorSupplier) {
- AggregatedPowerStatsConfig.PowerComponent config =
- new AggregatedPowerStatsConfig.PowerComponent(
- BatteryConsumer.POWER_COMPONENT_SCREEN)
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
.trackDeviceStates(STATE_POWER, STATE_SCREEN)
.trackUidStates(STATE_POWER, STATE_SCREEN)
.setProcessorSupplier(processorSupplier);
- PowerComponentAggregatedPowerStats aggregatedStats =
- new PowerComponentAggregatedPowerStats(
- new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+ PowerComponentAggregatedPowerStats aggregatedStats = new AggregatedPowerStats(config)
+ .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_SCREEN);
aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
@@ -260,7 +258,14 @@
return aggregatedStats;
}
- private void assertDevicePowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats,
+ private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
+ }
+
+ private void assertDevicePowerEstimate(
+ PowerComponentAggregatedPowerStats aggregatedStats,
int powerState, int screenState, double expectedScreenPowerEstimate,
double expectedDozePowerEstimate) {
PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
@@ -273,7 +278,8 @@
.of(expectedDozePowerEstimate);
}
- private void assertUidPowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats, int uid,
+ private void assertUidPowerEstimate(
+ PowerComponentAggregatedPowerStats aggregatedStats, int uid,
int powerState, int screenState, double expectedScreenPowerEstimate) {
PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(descriptor);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
similarity index 91%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
index 687d70b..d972604 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
@@ -14,19 +14,19 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
@@ -43,6 +43,8 @@
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.format.SensorPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -109,8 +111,7 @@
stats.finish(10000);
PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
- SensorPowerStatsLayout statsLayout = new SensorPowerStatsLayout();
- statsLayout.fromExtras(descriptor.extras);
+ SensorPowerStatsLayout statsLayout = new SensorPowerStatsLayout(descriptor);
String dump = stats.toString();
assertThat(dump).contains(" step_counter: ");
@@ -207,10 +208,8 @@
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessorSupplier(processorSupplier);
- AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
- PowerComponentAggregatedPowerStats powerComponentStats =
- aggregatedPowerStats.getPowerComponentStats(
- BatteryConsumer.POWER_COMPONENT_SENSORS);
+ PowerComponentAggregatedPowerStats powerComponentStats = new AggregatedPowerStats(config)
+ .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_SENSORS);
powerComponentStats.start(0);
powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
similarity index 91%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
index 11c09bc..baf468e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.processor;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.METERED_NO;
@@ -23,12 +23,12 @@
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import static com.google.common.truth.Truth.assertThat;
@@ -41,6 +41,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.net.NetworkStats;
import android.net.wifi.WifiManager;
@@ -53,6 +54,13 @@
import com.android.internal.os.Clock;
import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.MockBatteryStatsImpl;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.WifiPowerStatsCollector;
+import com.android.server.power.stats.WifiPowerStatsCollector.WifiStatsRetriever;
+import com.android.server.power.stats.format.WifiPowerStatsLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -61,7 +69,6 @@
import org.mockito.MockitoAnnotations;
import java.util.List;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class WifiPowerStatsProcessorTest {
@@ -109,8 +116,7 @@
private final SparseArray<ScanTimes> mScanTimes = new SparseArray<>();
private long mWifiActiveDuration;
- private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever =
- new WifiPowerStatsCollector.WifiStatsRetriever() {
+ private final WifiStatsRetriever mWifiStatsRetriever = new WifiStatsRetriever() {
@Override
public void retrieveWifiScanTimes(Callback callback) {
for (int i = 0; i < mScanTimes.size(); i++) {
@@ -159,11 +165,6 @@
}
@Override
- public IntSupplier getVoltageSupplier() {
- return () -> VOLTAGE_MV;
- }
-
- @Override
public Supplier<NetworkStats> getWifiNetworkStatsSupplier() {
return mNetworkStatsSupplier;
}
@@ -174,7 +175,7 @@
}
@Override
- public WifiPowerStatsCollector.WifiStatsRetriever getWifiStatsRetriever() {
+ public WifiStatsRetriever getWifiStatsRetriever() {
return mWifiStatsRetriever;
}
};
@@ -308,6 +309,7 @@
when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
// PowerStats hardware is available
+ when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
.thenReturn(new int[] {WIFI_ENERGY_CONSUMER_ID});
@@ -321,9 +323,9 @@
mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(0L,
WifiActivityEnergyInfo.STACK_STATE_INVALID, 0L, 0L, 0L, 0L));
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+ when(mConsumedEnergyRetriever.getConsumedEnergy(
new int[]{WIFI_ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{0});
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(0)});
aggregatedStats.start(0);
@@ -354,8 +356,8 @@
// 10 mAh represented as microWattSeconds
long energyUws = 10 * 3600 * VOLTAGE_MV;
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(
- new int[]{WIFI_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{WIFI_ENERGY_CONSUMER_ID}))
+ .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(energyUws)});
aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
@@ -525,15 +527,14 @@
private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
Supplier<PowerStatsProcessor> processorSupplier) {
- AggregatedPowerStatsConfig.PowerComponent config =
- new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_WIFI)
- .trackDeviceStates(STATE_POWER, STATE_SCREEN)
- .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
- .setProcessorSupplier(processorSupplier);
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_WIFI)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessorSupplier(processorSupplier);
- PowerComponentAggregatedPowerStats aggregatedStats =
- new PowerComponentAggregatedPowerStats(
- new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+ PowerComponentAggregatedPowerStats aggregatedStats = new AggregatedPowerStats(config)
+ .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_WIFI);
aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
@@ -547,6 +548,12 @@
return states;
}
+ private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+ EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.energyUWs = energyUWs;
+ return ecr;
+ }
+
private void mockWifiActivityEnergyInfo(WifiActivityEnergyInfo waei) {
doAnswer(invocation -> {
WifiManager.OnWifiActivityEnergyInfoListener
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index b58870b..895cd1e 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -36,6 +36,7 @@
"-Werror",
],
static_libs: [
+ "a11ychecker",
"aatf",
"accessibility_protos_lite",
"cts-input-lib",
@@ -273,21 +274,12 @@
"$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
}
-FLAKY = [
- "androidx.test.filters.FlakyTest",
-]
-
-FLAKY_AND_IGNORED = [
- "androidx.test.filters.FlakyTest",
- "org.junit.Ignore",
-]
// Used by content protection TEST_MAPPING
test_module_config {
name: "FrameworksServicesTests_contentprotection",
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.contentprotection"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -295,7 +287,6 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.om."],
- exclude_annotations: FLAKY_AND_IGNORED,
}
// Used by contexthub TEST_MAPPING
@@ -306,7 +297,6 @@
include_filters: ["com.android.server.location.contexthub."],
// TODO(ron): are these right, does it run anything?
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -316,7 +306,6 @@
include_filters: ["com.android.server.location.contexthub."],
// TODO(ron): are these right, does it run anything?
include_annotations: ["android.platform.test.annotations.Postsubmit"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
// Used by contentcapture
@@ -325,7 +314,6 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.contentcapture"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -333,7 +321,6 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.recoverysystem."],
- exclude_annotations: FLAKY,
}
// server pm TEST_MAPPING
@@ -343,7 +330,6 @@
test_suites: ["device-tests"],
include_annotations: ["android.platform.test.annotations.Presubmit"],
include_filters: ["com.android.server.pm."],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -352,7 +338,6 @@
test_suites: ["device-tests"],
include_annotations: ["android.platform.test.annotations.Postsubmit"],
include_filters: ["com.android.server.pm."],
- exclude_annotations: FLAKY_AND_IGNORED,
}
// server os TEST_MAPPING
@@ -368,7 +353,6 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -390,14 +374,6 @@
}
test_module_config {
- name: "FrameworksServicesTests_com_android_server_tare_Presubmit",
- base: "FrameworksServicesTests",
- test_suites: ["device-tests"],
- include_filters: ["com.android.server.tare"],
- exclude_annotations: FLAKY,
-}
-
-test_module_config {
name: "FrameworksServicesTests_com_android_server_tare",
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
@@ -405,14 +381,6 @@
}
test_module_config {
- name: "FrameworksServicesTests_com_android_server_usage_Presubmit",
- base: "FrameworksServicesTests",
- test_suites: ["device-tests"],
- include_filters: ["com.android.server.usage"],
- exclude_annotations: FLAKY,
-}
-
-test_module_config {
name: "FrameworksServicesTests_com_android_server_usage",
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
@@ -427,14 +395,6 @@
}
test_module_config {
- name: "FrameworksServicesTests_accessibility_Presubmit",
- base: "FrameworksServicesTests",
- test_suites: ["device-tests"],
- include_filters: ["com.android.server.accessibility"],
- exclude_annotations: FLAKY,
-}
-
-test_module_config {
name: "FrameworksServicesTests_accessibility",
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
@@ -462,7 +422,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.am."],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -485,7 +444,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.audio"],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -501,7 +459,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.hdmi"],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -509,7 +466,6 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.hdmi"],
- exclude_annotations: ["org.junit.Ignore"],
}
test_module_config {
@@ -524,7 +480,6 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.lights"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -540,7 +495,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.location.contexthub."],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -548,15 +502,6 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.locksettings."],
- exclude_annotations: FLAKY,
-}
-
-test_module_config {
- name: "FrameworksServicesTests_android_server_logcat_Presubmit",
- base: "FrameworksServicesTests",
- test_suites: ["device-tests"],
- include_filters: ["com.android.server.logcat"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -572,7 +517,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.net."],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -602,7 +546,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.policy."],
include_annotations: ["android.platform.test.annotations.Presubmit"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -624,7 +567,6 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.power.hint"],
- exclude_annotations: FLAKY,
}
test_module_config {
@@ -654,7 +596,6 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.location.contexthub."],
include_annotations: ["android.platform.test.annotations.Postsubmit"],
- exclude_annotations: FLAKY_AND_IGNORED,
}
test_module_config {
@@ -678,3 +619,176 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.input"],
}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_job",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.job"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_tare",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.tare"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_usage",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.usage"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_om",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.om"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_accessibility",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.accessibility"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_binarytransparencyservicetest",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.BinaryTransparencyServiceTest"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_pinnerservicetest",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.PinnerServiceTest"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_am",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.am."],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_hdmi",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.hdmi"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_logcat",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.logcat"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_net_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.net."],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_policy_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.policy."],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_policy",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.policy."],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_power",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.power"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_power_hint",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.power.hint"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_location_contexthub_Postsubmit",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.location.contexthub."],
+ include_annotations: ["android.platform.test.annotations.Postsubmit"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_server_input",
+ base: "FrameworksServicesTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.input"],
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index c8cbbb5..566feb7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -35,6 +35,7 @@
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE;
import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG;
import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER;
@@ -102,6 +103,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityWindowAttributes;
import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
@@ -137,6 +139,7 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import org.mockito.internal.util.reflection.FieldReader;
import org.mockito.internal.util.reflection.FieldSetter;
import org.mockito.stubbing.Answer;
@@ -209,6 +212,7 @@
@Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock private ProxyManager mProxyManager;
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+ @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback;
@Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
private IAccessibilityManager mA11yManagerServiceOnDevice;
private AccessibilityServiceConnection mAccessibilityServiceConnection;
@@ -2042,6 +2046,120 @@
.isEqualTo(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
}
+ @Test
+ public void showAccessibilityTargetSelection_navBarNavigationMode_softwareExtra() {
+ mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+ NAVIGATION_MODE, NAV_BAR_MODE_3BUTTON, userState.mUserId);
+
+ mA11yms.notifyAccessibilityButtonLongClicked(Display.DEFAULT_DISPLAY);
+ mTestableLooper.processAllMessages();
+
+ assertStartActivityWithExpectedShortcutType(mTestableContext.getMockContext(), SOFTWARE);
+ }
+
+ @Test
+ @DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void showAccessibilityTargetSelection_gestureNavigationMode_softwareExtra() {
+ mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+ NAVIGATION_MODE, NAV_BAR_MODE_GESTURAL, userState.mUserId);
+
+ mA11yms.notifyAccessibilityButtonLongClicked(Display.DEFAULT_DISPLAY);
+ mTestableLooper.processAllMessages();
+
+ assertStartActivityWithExpectedShortcutType(mTestableContext.getMockContext(), SOFTWARE);
+ }
+
+ @Test
+ @EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void showAccessibilityTargetSelection_gestureNavigationMode_gestureExtra() {
+ mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+ NAVIGATION_MODE, NAV_BAR_MODE_GESTURAL, userState.mUserId);
+
+ mA11yms.notifyAccessibilityButtonLongClicked(Display.DEFAULT_DISPLAY);
+ mTestableLooper.processAllMessages();
+
+ assertStartActivityWithExpectedShortcutType(mTestableContext.getMockContext(), GESTURE);
+ }
+
+ @Test
+ public void registerUserInitializationCompleteCallback_isRegistered() {
+ mA11yms.mUserInitializationCompleteCallbacks.clear();
+
+ mA11yms.registerUserInitializationCompleteCallback(mUserInitializationCompleteCallback);
+
+ assertThat(mA11yms.mUserInitializationCompleteCallbacks).containsExactly(
+ mUserInitializationCompleteCallback);
+ }
+
+ @Test
+ public void unregisterUserInitializationCompleteCallback_isUnregistered() {
+ mA11yms.mUserInitializationCompleteCallbacks.clear();
+ mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+
+ mA11yms.unregisterUserInitializationCompleteCallback(mUserInitializationCompleteCallback);
+
+ assertThat(mA11yms.mUserInitializationCompleteCallbacks).isEmpty();
+ }
+
+ @Test
+ public void switchUser_callsUserInitializationCompleteCallback() throws RemoteException {
+ mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+
+ mA11yms.switchUser(UserHandle.MIN_SECONDARY_USER_ID);
+
+ verify(mUserInitializationCompleteCallback).onUserInitializationComplete(
+ UserHandle.MIN_SECONDARY_USER_ID);
+ }
+
+ @Test
+ @DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void getShortcutTypeForGenericShortcutCalls_softwareType() {
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+
+ assertThat(mA11yms.getShortcutTypeForGenericShortcutCalls(userState.mUserId))
+ .isEqualTo(SOFTWARE);
+ }
+
+ @Test
+ @EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void getShortcutTypeForGenericShortcutCalls_gestureNavigationMode_gestureType() {
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+ NAVIGATION_MODE, NAV_BAR_MODE_GESTURAL, userState.mUserId);
+
+ assertThat(mA11yms.getShortcutTypeForGenericShortcutCalls(userState.mUserId))
+ .isEqualTo(GESTURE);
+ }
+
+ @Test
+ @EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void getShortcutTypeForGenericShortcutCalls_buttonNavigationMode_softwareType() {
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+ NAVIGATION_MODE, NAV_BAR_MODE_3BUTTON, userState.mUserId);
+
+ assertThat(mA11yms.getShortcutTypeForGenericShortcutCalls(userState.mUserId))
+ .isEqualTo(SOFTWARE);
+ }
+
private Set<String> readStringsFromSetting(String setting) {
final Set<String> result = new ArraySet<>();
mA11yms.readColonDelimitedSettingToSet(
@@ -2115,6 +2233,14 @@
Intent.EXTRA_COMPONENT_NAME)).isEqualTo(componentName);
}
+ private void assertStartActivityWithExpectedShortcutType(Context mockContext,
+ @UserShortcutType int shortcutType) {
+ verify(mockContext).startActivityAsUser(mIntentArgumentCaptor.capture(),
+ any(Bundle.class), any(UserHandle.class));
+ assertThat(mIntentArgumentCaptor.getValue().getIntExtra(
+ EXTRA_TYPE_TO_CHOOSE, -1)).isEqualTo(shortcutType);
+ }
+
private void setupShortcutTargetServices() {
setupShortcutTargetServices(mA11yms.getCurrentUserState());
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 62fa951..627b5e3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -28,10 +28,13 @@
import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
import static com.google.common.truth.Truth.assertThat;
@@ -67,6 +70,8 @@
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.util.test.FakeSettingsProvider;
import org.junit.After;
@@ -504,6 +509,36 @@
assertThat(actual).containsExactly(tileComponent, mMockServiceInfo);
}
+ @Test
+ public void isShortcutMagnificationEnabledLocked_anyShortcutType_returnsTrue() {
+ // Clear every shortcut
+ for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
+ setMagnificationForShortcutType(shortcutType, false);
+ }
+ // Check each shortcut individually
+ for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
+ // Setup
+ setMagnificationForShortcutType(shortcutType, true);
+
+ // Checking
+ assertThat(mUserState.getShortcutTargetsLocked(shortcutType))
+ .containsExactly(MAGNIFICATION_CONTROLLER_NAME);
+ assertThat(mUserState.isShortcutMagnificationEnabledLocked()).isTrue();
+
+ // Cleanup
+ setMagnificationForShortcutType(shortcutType, false);
+ }
+ }
+
+ @Test
+ public void isShortcutMagnificationEnabledLocked_noShortcutTypes_returnsFalse() {
+ // Clear every shortcut
+ for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
+ setMagnificationForShortcutType(shortcutType, false);
+ }
+ assertThat(mUserState.isShortcutMagnificationEnabledLocked()).isFalse();
+ }
+
private int getSecureIntForUser(String key, int userId) {
return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
}
@@ -511,4 +546,16 @@
private void putSecureIntForUser(String key, int value, int userId) {
Settings.Secure.putIntForUser(mMockResolver, key, value, userId);
}
+
+ private void setMagnificationForShortcutType(
+ @UserShortcutType int shortcutType, boolean enabled) {
+ if (shortcutType == TRIPLETAP) {
+ mUserState.setMagnificationSingleFingerTripleTapEnabledLocked(enabled);
+ } else if (shortcutType == TWOFINGER_DOUBLETAP) {
+ mUserState.setMagnificationTwoFingerTripleTapEnabledLocked(enabled);
+ } else {
+ mUserState.updateShortcutTargetsLocked(
+ enabled ? Set.of(MAGNIFICATION_CONTROLLER_NAME) : Set.of(), shortcutType);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
index 8753b25..019ccf9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
@@ -48,6 +48,7 @@
import java.util.LinkedList
import java.util.Queue
import android.util.ArraySet
+import android.view.InputDevice
/**
* Tests for {@link MouseKeysInterceptor}
@@ -68,6 +69,8 @@
}
private lateinit var mouseKeysInterceptor: MouseKeysInterceptor
+ private lateinit var inputDevice: InputDevice
+
private val clock = OffsettableClock()
private val testLooper = TestLooper { clock.now() }
private val nextInterceptor = TrackingInterceptor()
@@ -98,6 +101,10 @@
testSession = InputManagerGlobal.createTestSession(iInputManager)
mockInputManager = InputManager(context)
+ inputDevice = createInputDevice(DEVICE_ID)
+ Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID))
+ .thenReturn(inputDevice)
+
Mockito.`when`(mockVirtualDeviceManagerInternal.getDeviceIdsForUid(Mockito.anyInt()))
.thenReturn(ArraySet(setOf(DEVICE_ID)))
LocalServices.removeServiceForTest(VirtualDeviceManagerInternal::class.java)
@@ -115,7 +122,8 @@
Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
Mockito.`when`(mockAms.traceManager).thenReturn(mockTraceManager)
- mouseKeysInterceptor = MouseKeysInterceptor(mockAms, testLooper.looper, DISPLAY_ID)
+ mouseKeysInterceptor = MouseKeysInterceptor(mockAms, mockInputManager,
+ testLooper.looper, DISPLAY_ID)
mouseKeysInterceptor.next = nextInterceptor
}
@@ -281,6 +289,17 @@
}
}
+ private fun createInputDevice(
+ deviceId: Int,
+ generation: Int = -1
+ ): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setGeneration(generation)
+ .build()
+
private class TrackingInterceptor : BaseEventStreamTransformation() {
val events: Queue<KeyEvent> = LinkedList()
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
index 4ec2fb9..cdaeade 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
@@ -89,14 +89,15 @@
AccessibilityCheckResult.AccessibilityCheckResultType.NOT_RUN, null, 5,
null);
- Set<AndroidAccessibilityCheckerResult> results =
- AccessibilityCheckerUtils.processResults(
- mockNodeInfo,
- List.of(result1, result2, result3, result4),
- null,
+
+ AndroidAccessibilityCheckerResult.Builder resultBuilder =
+ AccessibilityCheckerUtils.getCommonResultBuilder(mockNodeInfo, null,
mMockPackageManager,
new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
TEST_A11Y_SERVICE_CLASS_NAME));
+ Set<AndroidAccessibilityCheckerResult> results =
+ AccessibilityCheckerUtils.processResults(mockNodeInfo,
+ List.of(result1, result2, result3, result4), resultBuilder);
assertThat(results).containsExactly(
createResult("TargetNode", "",
@@ -128,14 +129,14 @@
TouchTargetSizeCheck.class,
AccessibilityCheckResult.AccessibilityCheckResultType.ERROR, null, 2, null);
- Set<AndroidAccessibilityCheckerResult> results =
- AccessibilityCheckerUtils.processResults(
- mockNodeInfo,
- List.of(result1, result2),
- null,
+ AndroidAccessibilityCheckerResult.Builder resultBuilder =
+ AccessibilityCheckerUtils.getCommonResultBuilder(mockNodeInfo, null,
mMockPackageManager,
new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
TEST_A11Y_SERVICE_CLASS_NAME));
+ Set<AndroidAccessibilityCheckerResult> results =
+ AccessibilityCheckerUtils.processResults(mockNodeInfo,
+ List.of(result1, result2), resultBuilder);
assertThat(results).isEmpty();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 1cd61e9..e5005d1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -44,6 +44,8 @@
import android.graphics.PointF;
import android.os.Looper;
import android.os.SystemClock;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -56,6 +58,7 @@
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.utils.GestureLogParser;
import com.android.server.testutils.OffsettableClock;
@@ -119,6 +122,9 @@
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
/**
* {@link TouchExplorer#sendDownForAllNotInjectedPointers} injecting events with the same object
* is resulting {@link ArgumentCaptor} to capture events with last state. Before implementation
@@ -154,18 +160,43 @@
mHandler = new TestHandler();
mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
mTouchExplorer.setNext(mCaptor);
+ // Start TouchExplorer in the state where it has already reset InputDispatcher so that
+ // all tests do not start with an irrelevant ACTION_CANCEL.
+ mTouchExplorer.setHasResetInputDispatcherState(true);
}
@Test
public void testOneFingerMove_shouldInjectHoverEvents() {
- goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
- // Wait for transiting to touch exploring state.
+ triggerTouchExplorationWithOneFingerDownMoveUp();
+ assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ assertState(STATE_TOUCH_EXPLORING);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESET_INPUT_DISPATCHER_BEFORE_FIRST_TOUCH_EXPLORATION)
+ public void testStartTouchExploration_shouldResetInputDispatcherStateWithActionCancel() {
+ // Start TouchExplorer in the state where it has *not yet* reset InputDispatcher.
+ mTouchExplorer.setHasResetInputDispatcherState(false);
+ // Trigger touch exploration twice, with a handler fast-forward in between so TouchExplorer
+ // treats these as two separate interactions.
+ triggerTouchExplorationWithOneFingerDownMoveUp();
+ mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
+ triggerTouchExplorationWithOneFingerDownMoveUp();
+
+ assertCapturedEvents(
+ ACTION_CANCEL, // Only one ACTION_CANCEL before the first touch exploration
+ ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT,
+ ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ assertState(STATE_TOUCH_EXPLORING);
+ }
+
+ private void triggerTouchExplorationWithOneFingerDownMoveUp() {
+ send(downEvent());
+ // Fast forward so that TouchExplorer's timeouts transition us to the touch exploring state.
mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
moveEachPointers(mLastEvent, p(10, 10));
send(mLastEvent);
- goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
- assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
- assertState(STATE_TOUCH_EXPLORING);
+ send(upEvent());
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 957ee06..598d3a3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -23,6 +23,8 @@
import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
+import static android.view.MotionEvent.TOOL_TYPE_FINGER;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -1414,6 +1416,49 @@
}
@Test
+ public void testSynthesizedGestureEventsDoNotMoveMagnifierViewport() {
+ final EventCaptor eventCaptor = new EventCaptor();
+ mMgh.setNext(eventCaptor);
+
+ float centerX =
+ (INITIAL_MAGNIFICATION_BOUNDS.left + INITIAL_MAGNIFICATION_BOUNDS.width()) / 2.0f;
+ float centerY =
+ (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
+ float scale = 5.6f; // value is unimportant but unique among tests to increase coverage.
+ mFullScreenMagnificationController.setScaleAndCenter(
+ DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+ centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
+ centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
+
+ // Second finger down on trackpad starts a synthesized two-finger swipe with source
+ // mouse.
+ MotionEvent downEvent = motionEvent(centerX, centerY, ACTION_DOWN,
+ TOOL_TYPE_FINGER, CLASSIFICATION_TWO_FINGER_SWIPE);
+ send(downEvent, InputDevice.SOURCE_MOUSE);
+ fastForward(20);
+
+ // Two-finger swipe creates a synthesized move event, and shouldn't impact magnifier
+ // viewport.
+ MotionEvent moveEvent = motionEvent(centerX - 42, centerY - 42, ACTION_MOVE,
+ TOOL_TYPE_FINGER, CLASSIFICATION_TWO_FINGER_SWIPE);
+ send(moveEvent, InputDevice.SOURCE_MOUSE);
+ fastForward(20);
+
+ assertThat(mFullScreenMagnificationController.getCenterX(DISPLAY_0)).isEqualTo(centerX);
+ assertThat(mFullScreenMagnificationController.getCenterY(DISPLAY_0)).isEqualTo(centerY);
+
+ // The events were not consumed by magnifier.
+ assertThat(eventCaptor.mEvents.size()).isEqualTo(2);
+ assertThat(eventCaptor.mEvents.get(0).getSource()).isEqualTo(InputDevice.SOURCE_MOUSE);
+ assertThat(eventCaptor.mEvents.get(1).getSource()).isEqualTo(InputDevice.SOURCE_MOUSE);
+
+ final List<Integer> expectedActions = new ArrayList();
+ expectedActions.add(Integer.valueOf(ACTION_DOWN));
+ expectedActions.add(Integer.valueOf(ACTION_MOVE));
+ assertActionsInOrder(eventCaptor.mEvents, expectedActions);
+ }
+
+ @Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() {
runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
@@ -2130,6 +2175,30 @@
return MotionEvent.obtain(mLastDownTime, mClock.now(), action, x, y, 0);
}
+ private MotionEvent motionEvent(float x, float y, int action, int toolType,
+ int classification) {
+ // Create a generic motion event to populate the parameters.
+ MotionEvent event = motionEvent(x, y, action);
+ int pointerCount = event.getPointerCount();
+ MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount];
+ MotionEvent.PointerProperties[] properties =
+ new MotionEvent.PointerProperties[pointerCount];
+ for (int i = 0; i < pointerCount; i++) {
+ properties[i] = new MotionEvent.PointerProperties();
+ event.getPointerProperties(i, properties[i]);
+ properties[i].toolType = toolType;
+ coords[i] = new MotionEvent.PointerCoords();
+ event.getPointerCoords(i, coords[i]);
+ }
+ // Apply the custom classification.
+ return MotionEvent.obtain(event.getDownTime(), event.getEventTime(), action,
+ /*pointerCount=*/1, properties, coords,
+ event.getMetaState(), event.getButtonState(),
+ event.getXPrecision(), event.getYPrecision(), event.getDeviceId(),
+ event.getEdgeFlags(), event.getSource(), event.getDisplayId(), event.getFlags(),
+ classification);
+ }
+
private MotionEvent mouseEvent(float x, float y, int action) {
return fromMouse(motionEvent(x, y, action));
}
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/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 1db46bf..a25621a 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -55,6 +55,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
@@ -89,6 +90,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IRemoteCallback;
+import android.os.IpcDataCache;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManagerInternal;
@@ -145,7 +147,6 @@
*/
@SmallTest
@Presubmit
-
public class UserControllerTest {
// Use big enough user id to avoid picking up already active user id.
private static final int TEST_USER_ID = 100;
@@ -197,6 +198,9 @@
@Before
public void setUp() throws Exception {
runWithDexmakerShareClassLoader(() -> {
+ // Disable binder caches in this process.
+ IpcDataCache.disableForTestMode();
+
mInjector = spy(new TestInjector(getInstrumentation().getTargetContext()));
doNothing().when(mInjector).clearAllLockedTasks(anyString());
doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
@@ -593,6 +597,7 @@
@Test
public void testScheduleStopOfBackgroundUser_switch() {
mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+ assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -642,6 +647,7 @@
@Test
public void testScheduleStopOfBackgroundUser_startInBackground() throws Exception {
mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+ assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -681,6 +687,7 @@
@Test
public void testScheduleStopOfBackgroundUser_rescheduleWhenGuest() throws Exception {
mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+ assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -736,6 +743,7 @@
@Test
public void testScheduleStopOfBackgroundUser_rescheduleIfAlarm() throws Exception {
mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+ assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 6b8e414..b4b3612 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -558,7 +558,9 @@
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_NONE),
- eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE),
+ eq(Flags.mandatoryBiometrics()
+ ? BiometricConstants.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS
+ : BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE),
eq(0 /* vendorCode */));
// Enrolled, not disabled in settings, user requires confirmation in settings
@@ -1450,7 +1452,9 @@
}
@Test
- public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception {
+ @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testCanAuthenticate_whenBiometricsNotEnabledForApps_returnsHardwareUnavailable()
+ throws Exception {
setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false);
when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
@@ -1468,6 +1472,25 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception {
+ setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
+ when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
+
+ // When only biometric is requested
+ int authenticators = Authenticators.BIOMETRIC_STRONG;
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+
+ // When credential and biometric are requested
+ authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ }
+
+ @Test
public void testCanAuthenticate_whenNoBiometricSensor() throws Exception {
mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
mBiometricService.onStart();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index 4c3a233..b758f57 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -20,6 +20,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS;
import static com.android.server.biometrics.sensors.LockoutTracker.LOCKOUT_NONE;
@@ -264,6 +265,45 @@
assertThat(preAuthInfo.eligibleSensors).hasSize(0);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testCalculateByPriority()
+ throws Exception {
+ when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
+ when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false);
+
+ BiometricSensor faceSensor = getFaceSensor();
+ BiometricSensor fingerprintSensor = getFingerprintSensor();
+ PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setConfirmationRequested(false /* requireConfirmation */);
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
+ PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(faceSensor, fingerprintSensor),
+ 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+ assertThat(preAuthInfo.eligibleSensors).hasSize(0);
+ assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(
+ BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testMandatoryBiometricsNegativeButtonText_whenSet()
+ throws Exception {
+ when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+
+ final BiometricSensor sensor = getFaceSensor();
+ final PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME);
+ final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+ assertThat(promptInfo.getNegativeButtonText()).isEqualTo(TEST_PACKAGE_NAME);
+ }
+
private BiometricSensor getFingerprintSensor() {
BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT,
TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG,
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 5a78d9e..1a593dd 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -37,7 +37,6 @@
import android.app.WindowConfiguration;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
-import android.companion.virtual.VirtualDeviceManager;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
@@ -94,15 +93,9 @@
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock
- private VirtualDeviceManager.ActivityListener mActivityListener;
- @Mock
- private GenericWindowPolicyController.IntentListenerCallback mIntentListenerCallback;
- @Mock
- private GenericWindowPolicyController.ActivityBlockedCallback mActivityBlockedCallback;
+ private GenericWindowPolicyController.ActivityListener mActivityListener;
@Mock
private GenericWindowPolicyController.RunningAppsChangedListener mRunningAppsChangedListener;
- @Mock
- private GenericWindowPolicyController.SecureWindowCallback mSecureWindowCallback;
@Before
public void setUp() throws Exception {
@@ -669,14 +662,14 @@
/* targetDisplayCategory */ null);
// register interceptor and intercept intent
- when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(true);
+ when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(true);
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
/* isResultExpected = */ false, /* intentSender= */ null))
.isFalse();
// unregister interceptor and launch activity
- when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+ when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
/* isResultExpected = */ false, /* intentSender= */ null))
@@ -696,13 +689,12 @@
/* targetDisplayCategory */ null);
// register interceptor with different filter
- when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+ when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
/* isResultExpected = */ false, /* intentSender= */ null))
.isTrue();
- verify(mIntentListenerCallback, timeout(TIMEOUT_MILLIS))
- .shouldInterceptIntent(any(Intent.class));
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS)).shouldInterceptIntent(any(Intent.class));
}
@Test
@@ -723,8 +715,8 @@
/* isResultExpected = */ true, /* intentSender= */ () -> intentSender))
.isFalse();
- verify(mActivityBlockedCallback, timeout(TIMEOUT_MILLIS))
- .onActivityBlocked(DISPLAY_ID, activityInfo, /* intentSender= */ null);
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+ .onActivityLaunchBlocked(DISPLAY_ID, activityInfo, /* intentSender= */ null);
}
@Test
@@ -761,10 +753,10 @@
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue();
- verify(mSecureWindowCallback, after(TIMEOUT_MILLIS).never())
- .onSecureWindowShown(DISPLAY_ID, activityInfo.applicationInfo.uid);
- verify(mActivityBlockedCallback, never())
- .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, never())
+ .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
@Test
@@ -780,10 +772,10 @@
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, FLAG_SECURE, 0)).isTrue();
- verify(mSecureWindowCallback, timeout(TIMEOUT_MILLIS)).onSecureWindowShown(DISPLAY_ID,
- activityInfo.applicationInfo.uid);
- verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
- .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+ .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
@Test
@@ -800,10 +792,10 @@
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)).isTrue();
- verify(mSecureWindowCallback, after(TIMEOUT_MILLIS).never())
- .onSecureWindowShown(DISPLAY_ID, activityInfo.applicationInfo.uid);
- verify(mActivityBlockedCallback, never())
- .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, never())
+ .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
@Test
@@ -835,9 +827,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ mSecureWindowCallback,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -855,9 +844,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ mSecureWindowCallback,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -876,9 +862,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ homeComponent);
@@ -897,9 +880,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -918,9 +898,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -939,9 +916,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ Collections.singleton(displayCategory),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -960,9 +934,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ Collections.singleton(blockedComponent),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -981,9 +952,6 @@
/* crossTaskNavigationAllowedByDefault= */ false,
/* crossTaskNavigationExemptions= */ Collections.singleton(allowedComponent),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -1029,9 +997,9 @@
assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
isNewTask, /* isResultExpected= */ false, () -> intentSender)).isTrue();
- verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
- .onActivityBlocked(fromDisplay, activityInfo, intentSender);
- verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onActivityLaunchBlocked(fromDisplay, activityInfo, intentSender);
+ verify(mActivityListener, never()).shouldInterceptIntent(any(Intent.class));
}
private void assertActivityIsBlocked(GenericWindowPolicyController gwpc,
@@ -1046,9 +1014,9 @@
assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
isNewTask, /* isResultExpected= */ false, () -> intentSender)).isFalse();
- verify(mActivityBlockedCallback, timeout(TIMEOUT_MILLIS))
- .onActivityBlocked(fromDisplay, activityInfo, intentSender);
- verify(mIntentListenerCallback, after(TIMEOUT_MILLIS).never())
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+ .onActivityLaunchBlocked(fromDisplay, activityInfo, intentSender);
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.shouldInterceptIntent(any(Intent.class));
}
@@ -1060,8 +1028,8 @@
/* isResultExpected= */ false, () -> intentSender))
.isFalse();
- verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
- .onActivityBlocked(eq(fromDisplay), eq(activityInfo), any());
- verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onActivityLaunchBlocked(eq(fromDisplay), eq(activityInfo), any());
+ verify(mActivityListener, never()).shouldInterceptIntent(any(Intent.class));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index 405929a..51c2ad1 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -87,9 +87,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ null,
- /* activityBlockedCallback= */ null,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ null,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index 6ace9f1..fca0cfb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -20,6 +20,7 @@
import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_BOOT_UP;
+import static com.android.server.hdmi.HdmiCecFeatureAction.DELAY_GIVE_AUDIO_STATUS;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
@@ -582,6 +583,9 @@
);
mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(DELAY_GIVE_AUDIO_STATUS);
+ mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(
HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(),
getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
index 6731403..ec44a91 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
@@ -17,6 +17,7 @@
package com.android.server.hdmi;
import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
+import static com.android.server.hdmi.HdmiCecFeatureAction.DELAY_GIVE_AUDIO_STATUS;
import static com.google.common.truth.Truth.assertThat;
@@ -223,6 +224,9 @@
);
mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(DELAY_GIVE_AUDIO_STATUS);
+ mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(
HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(),
getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 004c6c6..21129a7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -19,6 +19,7 @@
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID;
@@ -529,21 +530,32 @@
public void handleVendorCommand_notHandled() {
HdmiCecMessage vendorCommand = HdmiCecMessageBuilder.buildVendorCommand(ADDR_TV,
ADDR_PLAYBACK_1, new byte[]{0});
- mNativeWrapper.onCecMessage(vendorCommand);
+ @Constants.HandleMessageResult int result =
+ mHdmiLocalDevice.handleVendorCommand(vendorCommand);
mTestLooper.dispatchAll();
- HdmiCecMessageBuilder.buildFeatureAbortCommand(ADDR_PLAYBACK_1, ADDR_TV,
- vendorCommand.getOpcode(), Constants.ABORT_REFUSED);
+ assertEquals(Constants.ABORT_REFUSED, result);
}
@Test
public void handleVendorCommandWithId_notHandled_Cec14() {
HdmiCecMessage vendorCommand = HdmiCecMessageBuilder.buildVendorCommandWithId(ADDR_TV,
ADDR_PLAYBACK_1, 0x1234, new byte[]{0});
- mNativeWrapper.onCecMessage(vendorCommand);
+ @Constants.HandleMessageResult int result =
+ mHdmiLocalDevice.handleVendorCommandWithId(vendorCommand);
mTestLooper.dispatchAll();
- HdmiCecMessageBuilder.buildFeatureAbortCommand(ADDR_PLAYBACK_1, ADDR_TV,
- vendorCommand.getOpcode(), Constants.ABORT_REFUSED);
+ assertEquals(Constants.ABORT_REFUSED, result);
+ }
+
+ @Test
+ public void handleVendorCommandWithId_broadcasted_handled() {
+ HdmiCecMessage vendorCommand = HdmiCecMessageBuilder.buildVendorCommandWithId(ADDR_TV,
+ ADDR_BROADCAST, 0x1234, new byte[]{0});
+ @Constants.HandleMessageResult int result =
+ mHdmiLocalDevice.handleVendorCommandWithId(vendorCommand);
+ mTestLooper.dispatchAll();
+
+ assertEquals(Constants.HANDLED, result);
}
}
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..2d95740 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,8 @@
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.android.server.hdmi.RequestSadAction.RETRY_COUNTER_MAX;
import static com.google.common.truth.Truth.assertThat;
@@ -77,6 +79,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 +218,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)
@@ -271,13 +274,12 @@
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
// Finish querying SADs
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mNativeWrapper.clearResultMessages();
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
+ for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
+ assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ }
assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
mNativeWrapper.clearResultMessages();
@@ -683,18 +685,46 @@
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
// Finish querying SADs
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mNativeWrapper.clearResultMessages();
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
+ for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
+ assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ }
assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
}
@Test
+ public void handleInitiateArc_arcAlreadyEstablished_noRequestSad() {
+ // Emulate Audio device on port 0x2000 (supports ARC)
+ mNativeWrapper.setPortConnectionStatus(2, true);
+ HdmiCecMessage reportPhysicalAddress =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mNativeWrapper.onCecMessage(reportPhysicalAddress);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceTv.isArcEstablished()).isFalse();
+
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildInitiateArc(
+ ADDR_AUDIO_SYSTEM,
+ ADDR_TV);
+ mNativeWrapper.onCecMessage(requestArcInitiation);
+ mTestLooper.dispatchAll();
+
+ // Finish querying SADs
+ for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
+ assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ }
+
+ assertThat(mHdmiCecLocalDeviceTv.isArcEstablished()).isTrue();
+ }
+
+ @Test
public void handleTerminateArc_noAudioDevice() {
HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc(
ADDR_AUDIO_SYSTEM,
@@ -968,13 +998,12 @@
// <Report ARC Initiated> should only be sent after SAD querying is done
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
// Finish querying SADs
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mNativeWrapper.clearResultMessages();
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
+ for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
+ assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ }
assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
mNativeWrapper.clearResultMessages();
@@ -1169,13 +1198,12 @@
mTestLooper.dispatchAll();
// Finish querying SADs
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mNativeWrapper.clearResultMessages();
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
+ for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
+ assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ }
// ARC should be established after RequestSadAction is finished
assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
@@ -1325,13 +1353,12 @@
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
// Finish querying SADs
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mNativeWrapper.clearResultMessages();
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
- mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
- mTestLooper.dispatchAll();
+ for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
+ assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ }
assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
}
@@ -2021,7 +2048,7 @@
ADDR_TV);
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isFalse();
mPowerManager.setInteractive(true);
mTestLooper.dispatchAll();
@@ -2031,14 +2058,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 +2078,7 @@
mHdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isFalse();
mPowerManager.setInteractive(true);
@@ -2060,7 +2087,7 @@
"HdmiCecLocalDeviceTvTest");
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isTrue();
assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
.isEqualTo(Constants.HANDLED);
@@ -2076,22 +2103,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 +2245,7 @@
@Override
protected int handleActiveSource(HdmiCecMessage message) {
- setWasActiveSourceSetToConnectedDevice(true);
+ setWasActivePathSetToConnectedDevice(true);
return super.handleActiveSource(message);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index f8e465c..4cf2937 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -18,6 +18,7 @@
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.RequestSadAction.RETRY_COUNTER_MAX;
import static com.google.common.truth.Truth.assertThat;
@@ -144,7 +145,7 @@
}
@Test
- public void noResponse_queryAgainOnce_emptyResult() {
+ public void noResponse_queryAgain_emptyResult() {
RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
mCallback);
action.start();
@@ -154,13 +155,13 @@
HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
- assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
- mNativeWrapper.clearResultMessages();
- mTestLooper.moveTimeForward(TIMEOUT_MS);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
- mTestLooper.moveTimeForward(TIMEOUT_MS);
- mTestLooper.dispatchAll();
+
+ for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ }
assertThat(mSupportedSads).isNotNull();
assertThat(mSupportedSads.size()).isEqualTo(0);
@@ -507,7 +508,7 @@
}
@Test
- public void invalidMessageLength_queryAgainOnce() {
+ public void invalidMessageLength_queryAgain() {
RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
mCallback);
action.start();
@@ -524,16 +525,13 @@
0x27, 0x20, 0x0A};
HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
- assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
- mNativeWrapper.clearResultMessages();
- action.processCommand(response1);
- mTestLooper.dispatchAll();
- mTestLooper.moveTimeForward(TIMEOUT_MS);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
- mNativeWrapper.clearResultMessages();
- mTestLooper.moveTimeForward(TIMEOUT_MS);
- mTestLooper.dispatchAll();
+
+ for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ }
assertThat(mSupportedSads).isNotNull();
assertThat(mSupportedSads.size()).isEqualTo(0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index b2fd8aa..161b18c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -165,7 +165,7 @@
assertTrue(resultContains(
callShellCommand("reset-throttling", "--user", "10"),
- "User 10 is not running or locked"));
+ "User (with userId=10) is not running or locked"));
mRunningUsers.put(USER_10, true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7912156..e652df5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -34,6 +34,7 @@
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.IpcDataCache;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -100,6 +101,9 @@
@Before
public void setUp() throws Exception {
+ // Disable binder caches in this process.
+ IpcDataCache.disableForTestMode();
+
mOriginalCurrentUserId = ActivityManager.getCurrentUser();
mUserManager = UserManager.get(mContext);
mActivityManager = mContext.getSystemService(ActivityManager.class);
diff --git a/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java
new file mode 100644
index 0000000..4aa6d39
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.server.rollback;
+
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class ApexdRevertLoggerTest {
+
+ private Context mMockContext = mock(Context.class);
+ private PackageManager mMockPm;
+ private PackageInfo mPackageInfo;
+
+ private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+ private static final int PACKAGE_INFO_FLAGS = PackageManager.MATCH_APEX
+ | PackageManager.GET_META_DATA;
+ private static final List<String> sFailingPackages =
+ List.of("package1", "package2", "package3");
+
+ @Before
+ public void setUp() {
+ mMockPm = mock(PackageManager.class);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+ PackageInstaller mockPi = mock(PackageInstaller.class);
+ when(mMockPm.getPackageInstaller()).thenReturn(mockPi);
+ PackageInstaller.SessionInfo mockSessionInfo = mock(PackageInstaller.SessionInfo.class);
+ when(mockPi.getSessionInfo(anyInt())).thenReturn(mockSessionInfo);
+ mPackageInfo = new PackageInfo();
+ }
+
+ /**
+ * Ensures that we make the correct Package Manager calls in the case that the failing packages
+ * are correctly configured with parent packages.
+ */
+ @Test
+ public void testApexdLoggingCallsWithParents() throws Exception {
+ for (String failingPackage: sFailingPackages) {
+ PackageInfo packageInfo = new PackageInfo();
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ Bundle bundle = new Bundle();
+ bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
+ applicationInfo.metaData = bundle;
+ packageInfo.applicationInfo = applicationInfo;
+ when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+ }
+
+ when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+ ApexdRevertLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+ for (String failingPackage: sFailingPackages) {
+ verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+ verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
+ }
+ }
+
+ /**
+ * Ensures that we don't make any calls to parent packages in the case that packages are not
+ * correctly configured with parent packages.
+ */
+ @Test
+ public void testApexdLoggingCallsWithNoParents() throws Exception {
+ for (String failingPackage: sFailingPackages) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+ }
+ when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+
+ ApexdRevertLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+ verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
+ for (String failingPackage: sFailingPackages) {
+ verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+ }
+ }
+
+ private String getParent(String packageName) {
+ return packageName + "-parent";
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
index d1c9643..8257168 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
@@ -20,7 +20,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -139,52 +138,4 @@
verify(mMockPm, times(1)).getPackageInfo(
sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
}
-
- /**
- * Ensures that we make the correct Package Manager calls in the case that the failing packages
- * are correctly configured with parent packages.
- */
- @Test
- public void testApexdLoggingCallsWithParents() throws Exception {
- for (String failingPackage: sFailingPackages) {
- PackageInfo packageInfo = new PackageInfo();
- ApplicationInfo applicationInfo = new ApplicationInfo();
- Bundle bundle = new Bundle();
- bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
- applicationInfo.metaData = bundle;
- packageInfo.applicationInfo = applicationInfo;
- when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
- }
-
- when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
- WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
- for (String failingPackage: sFailingPackages) {
- verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
- verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
- }
- }
-
- /**
- * Ensures that we don't make any calls to parent packages in the case that packages are not
- * correctly configured with parent packages.
- */
- @Test
- public void testApexdLoggingCallsWithNoParents() throws Exception {
- for (String failingPackage: sFailingPackages) {
- PackageInfo packageInfo = new PackageInfo();
- packageInfo.applicationInfo = new ApplicationInfo();
- when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
- }
- when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
-
- WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
- verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
- for (String failingPackage: sFailingPackages) {
- verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
- }
- }
-
- private String getParent(String packageName) {
- return packageName + "-parent";
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 963b27e..bf58443 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -38,6 +38,7 @@
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -69,6 +70,61 @@
private TunerResourceManagerService mTunerResourceManagerService;
private boolean mIsForeground;
+ private final class TunerClient extends IResourcesReclaimListener.Stub {
+ int[] mClientId;
+ ClientProfile mProfile;
+ boolean mReclaimed;
+
+ TunerClient() {
+ mClientId = new int[1];
+ mClientId[0] = TunerResourceManagerService.INVALID_CLIENT_ID;
+ }
+
+ public void register(String sessionId, int useCase) {
+ ResourceClientProfile profile = new ResourceClientProfile();
+ profile.tvInputSessionId = sessionId;
+ profile.useCase = useCase;
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, this, mClientId);
+ assertThat(mClientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mProfile = mTunerResourceManagerService.getClientProfile(mClientId[0]);
+ }
+
+ public void register(String sessionId, int useCase, int priority, int niceValue) {
+ register(sessionId, useCase);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ mClientId[0], priority, niceValue);
+ }
+
+ public void register(String sessionId, int useCase, int priority) {
+ register(sessionId, useCase, priority, 0);
+ }
+
+ public void unregister() {
+ mTunerResourceManagerService.unregisterClientProfileInternal(mClientId[0]);
+ mClientId[0] = TunerResourceManagerService.INVALID_CLIENT_ID;
+ mReclaimed = false;
+ }
+
+ public int getId() {
+ return mClientId[0];
+ }
+
+ public ClientProfile getProfile() {
+ return mProfile;
+ }
+
+ @Override
+ public void onReclaimResources() {
+ mTunerResourceManagerService.clearAllResourcesAndClientMapping(mProfile);
+ mReclaimed = true;
+ }
+
+ public boolean isReclaimed() {
+ return mReclaimed;
+ }
+ }
+
private static final class TestResourcesReclaimListener extends IResourcesReclaimListener.Stub {
boolean mReclaimed;
@@ -247,13 +303,11 @@
}
@Test
- public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() {
- ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile, null /*listener*/, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[1];
@@ -262,21 +316,20 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
+ client0.unregister();
}
@Test
- public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() {
- ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile, null /*listener*/, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
@@ -295,27 +348,23 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(0);
+ client0.unregister();
}
@Test
- public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() {
- ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile0, null /*listener*/, clientId0);
- mTunerResourceManagerService.registerClientProfileInternal(
- profile1, null /*listener*/, clientId1);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
@@ -335,13 +384,13 @@
int[] frontendHandle = new int[1];
TunerFrontendRequest request =
- tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
request =
- tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[1].handle);
@@ -349,31 +398,20 @@
.isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].handle).isInUse())
.isTrue();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() {
+ public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority()
+ throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 50};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 50);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -384,46 +422,36 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
request =
- tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
- assertThat(listener.isReclaimed()).isFalse();
+ assertThat(client0.isReclaimed()).isFalse();
request =
- tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
- assertThat(listener.isReclaimed()).isFalse();
+ assertThat(client0.isReclaimed()).isFalse();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() {
+ public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority()
+ throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 500};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -434,17 +462,16 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseFrontendHandles()).isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle, infos[1].handle)));
+ assertThat(client0.getProfile().getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(infos[0].handle, infos[1].handle)));
request =
- tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[1].handle);
@@ -453,22 +480,20 @@
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
- .getOwnerClientId()).isEqualTo(clientId1[0]);
+ .getOwnerClientId()).isEqualTo(client1.getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
- .getOwnerClientId()).isEqualTo(clientId1[0]);
- assertThat(listener.isReclaimed()).isTrue();
+ .getOwnerClientId()).isEqualTo(client1.getId());
+ assertThat(client0.isReclaimed()).isTrue();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void releaseFrontendTest_UnderTheSameExclusiveGroup() {
+ public void releaseFrontendTest_UnderTheSameExclusiveGroup() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -479,7 +504,7 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
@@ -488,43 +513,29 @@
.getFrontendResource(infos[1].handle).isInUse()).isTrue();
// Release frontend
- mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
- .getFrontendResource(frontendHandle[0]), clientId[0]);
+ mTunerResourceManagerService.releaseFrontendInternal(frontendHandle[0], client0.getId());
assertThat(mTunerResourceManagerService
.getFrontendResource(frontendHandle[0]).isInUse()).isFalse();
assertThat(mTunerResourceManagerService
.getFrontendResource(infos[1].handle).isInUse()).isFalse();
- assertThat(mTunerResourceManagerService
- .getClientProfile(clientId[0]).getInUseFrontendHandles().size()).isEqualTo(0);
+ assertThat(client0.getProfile().getInUseFrontendHandles().size()).isEqualTo(0);
+ client0.unregister();
}
@Test
- public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() {
+ public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 500};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- CasSessionRequest request = casSessionRequest(clientId0[0], 1 /*casSystemId*/);
+ CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
int[] casSessionHandle = new int[1];
// Request for 2 cas sessions.
assertThat(mTunerResourceManagerService
@@ -533,54 +544,45 @@
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseCasSystemId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCasSystemId())
+ .isEqualTo(1);
assertThat(mTunerResourceManagerService.getCasResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client0.getId())));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
- request = casSessionRequest(clientId1[0], 1);
+ request = casSessionRequest(client1.getId(), 1);
assertThat(mTunerResourceManagerService
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0])
- .getInUseCasSystemId()).isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(client1.getProfile().getInUseCasSystemId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCasSystemId())
+ .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
assertThat(mTunerResourceManagerService.getCasResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client1.getId())));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
- assertThat(listener.isReclaimed()).isTrue();
+ assertThat(client0.isReclaimed()).isTrue();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority() {
+ public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority()
+ throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 500};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
// Init cicam/cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- TunerCiCamRequest request = tunerCiCamRequest(clientId0[0], 1 /*ciCamId*/);
+ TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
int[] ciCamHandle = new int[1];
// Request for 2 ciCam sessions.
assertThat(mTunerResourceManagerService
@@ -589,139 +591,125 @@
.requestCiCamInternal(request, ciCamHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseCiCamId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1);
assertThat(mTunerResourceManagerService.getCiCamResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client0.getId())));
assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue();
- request = tunerCiCamRequest(clientId1[0], 1);
+ request = tunerCiCamRequest(client1.getId(), 1);
assertThat(mTunerResourceManagerService
.requestCiCamInternal(request, ciCamHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0])
- .getInUseCiCamId()).isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(client1.getProfile().getInUseCiCamId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCiCamId())
+ .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
assertThat(mTunerResourceManagerService.getCiCamResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
- assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
- assertThat(listener.isReclaimed()).isTrue();
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client1.getId())));
+ assertThat(mTunerResourceManagerService
+ .getCiCamResource(1).isFullyUsed()).isFalse();
+ assertThat(client0.isReclaimed()).isTrue();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void releaseCasTest() {
+ public void releaseCasTest() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- CasSessionRequest request = casSessionRequest(clientId[0], 1 /*casSystemId*/);
+ CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
int[] casSessionHandle = new int[1];
// Request for 1 cas sessions.
assertThat(mTunerResourceManagerService
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
- .getInUseCasSystemId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCasSystemId()).isEqualTo(1);
assertThat(mTunerResourceManagerService.getCasResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0])));
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client0.getId())));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
// Release cas
mTunerResourceManagerService.releaseCasSessionInternal(mTunerResourceManagerService
- .getCasResource(1), clientId[0]);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
- .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ .getCasResource(1), client0.getId());
+ assertThat(client0.getProfile().getInUseCasSystemId())
+ .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
assertThat(mTunerResourceManagerService.getCasResource(1)
.getOwnerClientIds()).isEmpty();
+ client0.unregister();
}
@Test
- public void releaseCiCamTest() {
+ public void releaseCiCamTest() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- TunerCiCamRequest request = tunerCiCamRequest(clientId[0], 1 /*ciCamId*/);
+ TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
int[] ciCamHandle = new int[1];
// Request for 1 ciCam sessions.
assertThat(mTunerResourceManagerService
.requestCiCamInternal(request, ciCamHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
- .getInUseCiCamId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1);
assertThat(mTunerResourceManagerService.getCiCamResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0])));
- assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client0.getId())));
+ assertThat(mTunerResourceManagerService
+ .getCiCamResource(1).isFullyUsed()).isFalse();
// Release ciCam
mTunerResourceManagerService.releaseCiCamInternal(mTunerResourceManagerService
- .getCiCamResource(1), clientId[0]);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
- .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
- assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+ .getCiCamResource(1), client0.getId());
+ assertThat(client0.getProfile().getInUseCiCamId())
+ .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(mTunerResourceManagerService
+ .getCiCamResource(1).isFullyUsed()).isFalse();
assertThat(mTunerResourceManagerService.getCiCamResource(1)
.getOwnerClientIds()).isEmpty();
+ client0.unregister();
}
@Test
- public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() {
+ public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 500};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
// Init lnb resources.
int[] lnbHandles = {1};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
TunerLnbRequest request = new TunerLnbRequest();
- request.clientId = clientId0[0];
+ request.clientId = client0.getId();
int[] lnbHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]).getInUseLnbHandles())
+ assertThat(client0.getProfile().getInUseLnbHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0])));
request = new TunerLnbRequest();
- request.clientId = clientId1[0];
+ request.clientId = client1.getId();
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
@@ -729,29 +717,26 @@
assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0])
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0])
- .getOwnerClientId()).isEqualTo(clientId1[0]);
- assertThat(listener.isReclaimed()).isTrue();
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseLnbHandles().size()).isEqualTo(0);
+ .getOwnerClientId()).isEqualTo(client1.getId());
+ assertThat(client0.isReclaimed()).isTrue();
+ assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void releaseLnbTest() {
+ public void releaseLnbTest() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init lnb resources.
int[] lnbHandles = {0};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
TunerLnbRequest request = new TunerLnbRequest();
- request.clientId = clientId[0];
+ request.clientId = client0.getId();
int[] lnbHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
@@ -762,19 +747,16 @@
.getLnbResource(lnbHandle[0]));
assertThat(mTunerResourceManagerService
.getLnbResource(lnbHandle[0]).isInUse()).isFalse();
- assertThat(mTunerResourceManagerService
- .getClientProfile(clientId[0]).getInUseLnbHandles().size()).isEqualTo(0);
+ assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
+ client0.unregister();
}
@Test
- public void unregisterClientTest_usingFrontend() {
- // Register client
- ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile, null /*listener*/, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void unregisterClientTest_usingFrontend() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -785,7 +767,7 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
@@ -796,26 +778,20 @@
.isInUse()).isTrue();
// Unregister client when using frontend
- mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]);
+ client0.unregister();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isFalse();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
- assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse();
-
+ assertThat(mTunerResourceManagerService.checkClientExists(client0.getId())).isFalse();
}
@Test
- public void requestDemuxTest() {
- // Register client
- ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId0 = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile0, null /*listener*/, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void requestDemuxTest() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
TunerDemuxInfo[] infos = new TunerDemuxInfo[3];
infos[0] = tunerDemuxInfo(0 /* handle */, Filter.TYPE_TS | Filter.TYPE_IP);
@@ -825,7 +801,7 @@
int[] demuxHandle0 = new int[1];
// first with undefined type (should be the first one with least # of caps)
- TunerDemuxRequest request = tunerDemuxRequest(clientId0[0], Filter.TYPE_UNDEFINED);
+ TunerDemuxRequest request = tunerDemuxRequest(client0.getId(), Filter.TYPE_UNDEFINED);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
.isTrue();
assertThat(demuxHandle0[0]).isEqualTo(1);
@@ -846,16 +822,16 @@
assertThat(demuxHandle0[0]).isEqualTo(2);
// request for another TS
- int[] clientId1 = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile1, null /*listener*/, clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client1 = new TunerClient();
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+
int[] demuxHandle1 = new int[1];
- TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_TS);
+ TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_TS);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request1, demuxHandle1))
.isTrue();
assertThat(demuxHandle1[0]).isEqualTo(0);
- assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle1[0]))
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(client1.getId()))
.isEqualTo(0);
// release demuxes
@@ -863,33 +839,23 @@
mTunerResourceManagerService.releaseDemuxInternal(dr);
dr = mTunerResourceManagerService.getDemuxResource(demuxHandle1[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
+
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void requestDemuxTest_ResourceReclaim() {
+ public void requestDemuxTest_ResourceReclaim() throws RemoteException {
// Register clients
- ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
- ResourceClientProfile profile2 = resourceClientProfile("2" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- int[] clientId2 = new int[1];
- TestResourcesReclaimListener listener0 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener listener1 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener listener2 = new TestResourcesReclaimListener();
-
- mTunerResourceManagerService.registerClientProfileInternal(
- profile0, listener0, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.registerClientProfileInternal(
- profile1, listener1, clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.registerClientProfileInternal(
- profile2, listener2, clientId1);
- assertThat(clientId2[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ TunerClient client2 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
+ client2.register("2" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
// Init demux resources.
TunerDemuxInfo[] infos = new TunerDemuxInfo[2];
@@ -897,66 +863,67 @@
infos[1] = tunerDemuxInfo(1 /*handle*/, Filter.TYPE_TS);
mTunerResourceManagerService.setDemuxInfoListInternal(infos);
- // let clientId0(prio:100) request for IP - should succeed
- TunerDemuxRequest request0 = tunerDemuxRequest(clientId0[0], Filter.TYPE_IP);
+ // let client0(prio:100) request for IP - should succeed
+ TunerDemuxRequest request0 = tunerDemuxRequest(client0.getId(), Filter.TYPE_IP);
int[] demuxHandle0 = new int[1];
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request0, demuxHandle0)).isTrue();
assertThat(demuxHandle0[0]).isEqualTo(0);
- // let clientId1(prio:50) request for IP - should fail
- TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_IP);
+ // let client1(prio:50) request for IP - should fail
+ TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_IP);
int[] demuxHandle1 = new int[1];
demuxHandle1[0] = -1;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request1, demuxHandle1)).isFalse();
- assertThat(listener0.isReclaimed()).isFalse();
+ assertThat(client0.isReclaimed()).isFalse();
assertThat(demuxHandle1[0]).isEqualTo(-1);
- // let clientId1(prio:50) request for TS - should succeed
+ // let client1(prio:50) request for TS - should succeed
request1.desiredFilterTypes = Filter.TYPE_TS;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request1, demuxHandle1)).isTrue();
assertThat(demuxHandle1[0]).isEqualTo(1);
- assertThat(listener0.isReclaimed()).isFalse();
+ assertThat(client0.isReclaimed()).isFalse();
- // now release demux for the clientId0 (higher priority) and request demux
+ // now release demux for the client0 (higher priority) and request demux
DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
- // let clientId2(prio:50) request for TS - should succeed
- TunerDemuxRequest request2 = tunerDemuxRequest(clientId2[0], Filter.TYPE_TS);
+ // let client2(prio:50) request for TS - should succeed
+ TunerDemuxRequest request2 = tunerDemuxRequest(client2.getId(), Filter.TYPE_TS);
int[] demuxHandle2 = new int[1];
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request2, demuxHandle2)).isTrue();
assertThat(demuxHandle2[0]).isEqualTo(0);
- assertThat(listener1.isReclaimed()).isFalse();
+ assertThat(client1.isReclaimed()).isFalse();
- // let clientId0(prio:100) request for TS - should reclaim from clientId2
+ // let client0(prio:100) request for TS - should reclaim from client1
// , who has the smaller caps
request0.desiredFilterTypes = Filter.TYPE_TS;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request0, demuxHandle0)).isTrue();
- assertThat(listener1.isReclaimed()).isFalse();
- assertThat(listener2.isReclaimed()).isTrue();
+ assertThat(client1.isReclaimed()).isTrue();
+ assertThat(client2.isReclaimed()).isFalse();
+ client0.unregister();
+ client1.unregister();
+ client2.unregister();
}
@Test
public void requestDescramblerTest() {
- // Register client
- ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile, null /*listener*/, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] desHandle = new int[1];
TunerDescramblerRequest request = new TunerDescramblerRequest();
- request.clientId = clientId[0];
+ request.clientId = client0.getId();
assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
.isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
+ client0.unregister();
}
@Test
@@ -978,74 +945,26 @@
}
@Test
- public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() {
+ public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() throws RemoteException {
/**** Register Clients and Set Priority ****/
-
- // Int array to save the returned client ids
- int[] ownerClientId0 = new int[1];
- int[] ownerClientId1 = new int[1];
- int[] shareClientId0 = new int[1];
- int[] shareClientId1 = new int[1];
-
- // Predefined client profiles
- ResourceClientProfile[] ownerProfiles = new ResourceClientProfile[2];
- ResourceClientProfile[] shareProfiles = new ResourceClientProfile[2];
- ownerProfiles[0] = resourceClientProfile(
- "0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
- ownerProfiles[1] = resourceClientProfile(
- "1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
- shareProfiles[0] = resourceClientProfile(
- "2" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
- shareProfiles[1] = resourceClientProfile(
- "3" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
-
- // Predefined client reclaim listeners
- TestResourcesReclaimListener ownerListener0 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener shareListener0 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener ownerListener1 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener shareListener1 = new TestResourcesReclaimListener();
- // Register clients and validate the returned client ids
- mTunerResourceManagerService
- .registerClientProfileInternal(ownerProfiles[0], ownerListener0, ownerClientId0);
- mTunerResourceManagerService
- .registerClientProfileInternal(shareProfiles[0], shareListener0, shareClientId0);
- mTunerResourceManagerService
- .registerClientProfileInternal(ownerProfiles[1], ownerListener1, ownerClientId1);
- mTunerResourceManagerService
- .registerClientProfileInternal(shareProfiles[1], shareListener1, shareClientId1);
- assertThat(ownerClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- assertThat(shareClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- assertThat(ownerClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- assertThat(shareClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient ownerClient0 = new TunerClient();
+ TunerClient ownerClient1 = new TunerClient();
+ TunerClient shareClient0 = new TunerClient();
+ TunerClient shareClient1 = new TunerClient();
+ ownerClient0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 100);
+ ownerClient1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 300);
+ shareClient0.register("2" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 200);
+ shareClient1.register("3" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 400);
mTunerResourceManagerService.updateClientPriorityInternal(
- ownerClientId0[0],
- 100/*priority*/,
- 0/*niceValue*/);
- mTunerResourceManagerService.updateClientPriorityInternal(
- shareClientId0[0],
- 200/*priority*/,
- 0/*niceValue*/);
- mTunerResourceManagerService.updateClientPriorityInternal(
- ownerClientId1[0],
- 300/*priority*/,
- 0/*niceValue*/);
- mTunerResourceManagerService.updateClientPriorityInternal(
- shareClientId1[0],
- 400/*priority*/,
- 0/*niceValue*/);
- mTunerResourceManagerService.updateClientPriorityInternal(
- shareClientId1[0],
+ shareClient1.getId(),
-1/*invalid priority*/,
0/*niceValue*/);
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId1[0])
- .getPriority())
- .isEqualTo(400);
+ assertThat(shareClient1.getProfile().getPriority()).isEqualTo(400);
/**** Init Frontend Resources ****/
@@ -1072,7 +991,7 @@
// Predefined frontend request and array to save returned frontend handle
int[] frontendHandle = new int[1];
TunerFrontendRequest request = tunerFrontendRequest(
- ownerClientId0[0] /*clientId*/,
+ ownerClient0.getId() /*clientId*/,
FrontendSettings.TYPE_DVBT);
// Request call and validate granted resource and internal mapping
@@ -1080,9 +999,7 @@
.requestFrontendInternal(request, frontendHandle))
.isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
+ assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
@@ -1091,11 +1008,11 @@
// Share frontend call and validate the internal mapping
mTunerResourceManagerService.shareFrontendInternal(
- shareClientId0[0]/*selfClientId*/,
- ownerClientId0[0]/*targetClientId*/);
+ shareClient0.getId()/*selfClientId*/,
+ ownerClient0.getId()/*targetClientId*/);
mTunerResourceManagerService.shareFrontendInternal(
- shareClientId1[0]/*selfClientId*/,
- ownerClientId0[0]/*targetClientId*/);
+ shareClient1.getId()/*selfClientId*/,
+ ownerClient0.getId()/*targetClientId*/);
// Verify fe in use status
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isTrue();
@@ -1103,31 +1020,24 @@
.isInUse()).isTrue();
// Verify fe owner status
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
- .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
+ .getOwnerClientId()).isEqualTo(ownerClient0.getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
- .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
+ .getOwnerClientId()).isEqualTo(ownerClient0.getId());
// Verify share fe client status in the primary owner client
- assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
- .getShareFeClientIds())
+ assertThat(ownerClient0.getProfile().getShareFeClientIds())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- shareClientId0[0],
- shareClientId1[0])));
+ shareClient0.getId(),
+ shareClient1.getId())));
// Verify in use frontend list in all the primary owner and share owner clients
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
+ assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles())
+ assertThat(shareClient0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId1[0])
- .getInUseFrontendHandles())
+ assertThat(shareClient1.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
@@ -1135,21 +1045,17 @@
/**** Remove Frontend Share Owner ****/
// Unregister the second share fe client
- mTunerResourceManagerService.unregisterClientProfileInternal(shareClientId1[0]);
+ shareClient1.unregister();
// Validate the internal mapping
- assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
- .getShareFeClientIds())
+ assertThat(ownerClient0.getProfile().getShareFeClientIds())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- shareClientId0[0])));
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
+ shareClient0.getId())));
+ assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
+ assertThat(shareClient0.getProfile()
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
@@ -1159,7 +1065,7 @@
// Predefined second frontend request
request = tunerFrontendRequest(
- ownerClientId1[0] /*clientId*/,
+ ownerClient1.getId() /*clientId*/,
FrontendSettings.TYPE_DVBT);
// Second request call
@@ -1170,43 +1076,35 @@
// Validate granted resource and internal mapping
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
- .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
+ .getOwnerClientId()).isEqualTo(ownerClient1.getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
- .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId1[0])
- .getInUseFrontendHandles())
+ .getOwnerClientId()).isEqualTo(ownerClient1.getId());
+ assertThat(ownerClient1.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles()
+ assertThat(ownerClient0.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles()
+ assertThat(shareClient0.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getShareFeClientIds()
+ assertThat(ownerClient0.getProfile().getShareFeClientIds()
.isEmpty())
.isTrue();
- assertThat(ownerListener0.isReclaimed()).isTrue();
- assertThat(shareListener0.isReclaimed()).isTrue();
+ assertThat(ownerClient0.isReclaimed()).isTrue();
+ assertThat(shareClient0.isReclaimed()).isTrue();
/**** Release Frontend Resource From Primary Owner ****/
// Reshare the frontend
mTunerResourceManagerService.shareFrontendInternal(
- shareClientId0[0]/*selfClientId*/,
- ownerClientId1[0]/*targetClientId*/);
+ shareClient0.getId()/*selfClientId*/,
+ ownerClient1.getId()/*targetClientId*/);
// Release the frontend resource from the primary owner
- mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
- .getFrontendResource(infos[0].handle), ownerClientId1[0]);
+ mTunerResourceManagerService.releaseFrontendInternal(infos[0].handle,
+ ownerClient1.getId());
// Validate the internal mapping
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
@@ -1214,19 +1112,13 @@
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
// Verify client status
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId1[0])
- .getInUseFrontendHandles()
+ assertThat(ownerClient1.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles()
+ assertThat(shareClient0.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId1[0])
- .getShareFeClientIds()
+ assertThat(ownerClient1.getProfile().getShareFeClientIds()
.isEmpty())
.isTrue();
@@ -1234,7 +1126,7 @@
// Predefined Lnb request and handle array
TunerLnbRequest requestLnb = new TunerLnbRequest();
- requestLnb.clientId = shareClientId0[0];
+ requestLnb.clientId = shareClient0.getId();
int[] lnbHandle = new int[1];
// Request for an Lnb
@@ -1247,11 +1139,11 @@
.requestFrontendInternal(request, frontendHandle))
.isTrue();
mTunerResourceManagerService.shareFrontendInternal(
- shareClientId0[0]/*selfClientId*/,
- ownerClientId1[0]/*targetClientId*/);
+ shareClient0.getId()/*selfClientId*/,
+ ownerClient1.getId()/*targetClientId*/);
// Unregister the primary owner of the shared frontend
- mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]);
+ ownerClient1.unregister();
// Validate the internal mapping
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
@@ -1259,16 +1151,15 @@
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
// Verify client status
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles()
+ assertThat(shareClient0.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseLnbHandles())
+ assertThat(shareClient0.getProfile().getInUseLnbHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
lnbHandles[0])));
+
+ ownerClient0.unregister();
+ shareClient0.unregister();
}
private TunerFrontendInfo tunerFrontendInfo(
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index cbf7935..def3355 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -19,7 +19,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
+import android.os.UserHandle;
import android.webkit.UserPackage;
import android.webkit.WebViewProviderInfo;
@@ -137,16 +137,12 @@
List<UserPackage> ret = new ArrayList();
// Loop over defined users, and find the corresponding package for each user.
for (int userId : mUsers) {
- ret.add(new UserPackage(createUserInfo(userId),
+ ret.add(new UserPackage(UserHandle.of(userId),
userPackages == null ? null : userPackages.get(userId)));
}
return ret;
}
- private static UserInfo createUserInfo(int userId) {
- return new UserInfo(userId, "User nr. " + userId, 0 /* flags */);
- }
-
/**
* Set package for primary user.
*/
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/timetests/Android.bp b/services/tests/timetests/Android.bp
index 7a4b974..aae6acc 100644
--- a/services/tests/timetests/Android.bp
+++ b/services/tests/timetests/Android.bp
@@ -25,3 +25,27 @@
certificate: "platform",
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "FrameworksTimeServicesTests_time",
+ base: "FrameworksTimeServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: [
+ "com.android.server.timezonedetector.",
+ "com.android.server.timedetector.",
+ ],
+}
+
+test_module_config {
+ name: "FrameworksTimeServicesTests_server_timedetector",
+ base: "FrameworksTimeServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.timedetector."],
+}
+
+test_module_config {
+ name: "FrameworksTimeServicesTests_server_timezonedetector",
+ base: "FrameworksTimeServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.timezonedetector."],
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index b3ec215..c9d5241 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -30,6 +30,7 @@
import androidx.test.InstrumentationRegistry;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.After;
@@ -41,6 +42,7 @@
public class UiServiceTestCase {
@Mock protected PackageManagerInternal mPmi;
+ @Mock protected UserManagerInternal mUmi;
@Mock protected UriGrantsManagerInternal mUgmInternal;
protected static final String PKG_N_MR1 = "com.example.n_mr1";
@@ -92,6 +94,8 @@
}
});
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, mUmi);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
when(mUgmInternal.checkGrantUriPermission(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
index b5bc610..2effc69 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
@@ -30,9 +30,9 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -44,6 +44,8 @@
import android.app.Person;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -89,6 +91,8 @@
@Mock
ShortcutHelper mShortcutHelper;
@Mock
+ PackageManager mPackageManager;
+ @Mock
ActivityManager mActivityManager;
@Before
@@ -98,6 +102,7 @@
mBubbleExtractor.initialize(mContext, mock(NotificationUsageStats.class));
mBubbleExtractor.setConfig(mConfig);
mBubbleExtractor.setShortcutHelper(mShortcutHelper);
+ mBubbleExtractor.setPackageManager(mPackageManager);
mBubbleExtractor.setActivityManager(mActivityManager);
mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, IMPORTANCE_DEFAULT);
@@ -106,7 +111,7 @@
}
/* NotificationRecord that fulfills conversation requirements (message style + shortcut) */
- private NotificationRecord getNotificationRecord(boolean addBubble) {
+ private NotificationRecord getNotificationRecord(boolean addBubble, UserHandle user) {
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -127,13 +132,13 @@
n.setBubbleMetadata(mBubbleMetadata);
}
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, ID, TAG, UID,
- PID, n, mUser, null, System.currentTimeMillis());
+ PID, n, user, null, System.currentTimeMillis());
NotificationRecord r = new NotificationRecord(getContext(), sbn, mChannel);
r.setShortcutInfo(mShortcutInfo);
return r;
}
- void setUpIntentBubble(boolean isValid) {
+ void setUpIntentBubble(boolean isValid, UserHandle user) {
when(mPendingIntent.getIntent()).thenReturn(mIntent);
when(mBubbleMetadata.getIntent()).thenReturn(mPendingIntent);
when(mBubbleMetadata.getShortcutId()).thenReturn(null);
@@ -143,18 +148,21 @@
info.resizeMode = isValid
? RESIZE_MODE_RESIZEABLE
: RESIZE_MODE_UNRESIZEABLE;
- when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(info);
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = info;
+ when(mPackageManager.resolveActivityAsUser(eq(mIntent), eq(0), eq(user.getIdentifier())))
+ .thenReturn(resolveInfo);
}
- void setUpShortcutBubble(boolean isValid) {
+ void setUpShortcutBubble(boolean isValid, UserHandle user) {
when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
when(mBubbleMetadata.getIntent()).thenReturn(null);
ShortcutInfo answer = isValid ? mShortcutInfo : null;
- when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, mUser)).thenReturn(answer);
+ when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, user)).thenReturn(answer);
}
- void setUpBubblesEnabled(boolean feature, int app, int channel) {
- when(mConfig.bubblesEnabled(mUser)).thenReturn(feature);
+ void setUpBubblesEnabled(boolean feature, int app, int channel, UserHandle user) {
+ when(mConfig.bubblesEnabled(user)).thenReturn(feature);
when(mConfig.getBubblePreference(anyString(), anyInt())).thenReturn(app);
mChannel.setAllowBubbles(channel);
}
@@ -167,10 +175,11 @@
public void testAppYesChannelNo() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- ALLOW_BUBBLE_OFF /* channel */);
+ ALLOW_BUBBLE_OFF /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ setUpShortcutBubble(true /* isValid */, mUser);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
@@ -181,10 +190,11 @@
public void testAppYesChannelDefault() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ setUpShortcutBubble(true /* isValid */, mUser);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -195,10 +205,11 @@
public void testAppYesChannelYes() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- ALLOW_BUBBLE_ON /* channel */);
+ ALLOW_BUBBLE_ON /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ setUpShortcutBubble(true /* isValid */, mUser);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -209,10 +220,11 @@
public void testAppYesChannelYesFeatureNo() {
setUpBubblesEnabled(false /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- ALLOW_BUBBLE_ON /* channel */);
+ ALLOW_BUBBLE_ON /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ setUpShortcutBubble(true /* isValid */, mUser);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -224,10 +236,11 @@
public void testAppNoChannelYes() throws Exception {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_NONE /* app */,
- ALLOW_BUBBLE_ON /* channel */);
+ ALLOW_BUBBLE_ON /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ setUpShortcutBubble(true /* isValid */, mUser);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -239,10 +252,11 @@
public void testAppNoChannelDefault() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_NONE /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ setUpShortcutBubble(true /* isValid */, mUser);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -254,10 +268,11 @@
public void testAppSelectedChannelDefault() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_SELECTED /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ setUpShortcutBubble(true /* isValid */, mUser);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -269,10 +284,11 @@
public void testAppSelectedChannelNo() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_SELECTED /* app */,
- ALLOW_BUBBLE_OFF /* channel */);
+ ALLOW_BUBBLE_OFF /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ setUpShortcutBubble(true /* isValid */, mUser);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -284,11 +300,12 @@
public void testAppSeletedChannelYes() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_SELECTED /* app */,
- ALLOW_BUBBLE_ON /* channel */);
+ ALLOW_BUBBLE_ON /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
+ setUpShortcutBubble(true /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -299,11 +316,12 @@
public void testAppSeletedChannelYesFeatureNo() {
setUpBubblesEnabled(false /* feature */,
BUBBLE_PREFERENCE_SELECTED /* app */,
- ALLOW_BUBBLE_ON /* channel */);
+ ALLOW_BUBBLE_ON /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
+ setUpShortcutBubble(true /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
@@ -319,11 +337,12 @@
public void testFlagBubble_false_previouslyRemoved() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
+ setUpShortcutBubble(true /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
r.setFlagBubbleRemoved(true);
mBubbleExtractor.process(r);
@@ -337,11 +356,12 @@
public void testFlagBubble_true_shortcutBubble() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(true /* isValid */);
+ setUpShortcutBubble(true /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
assertTrue(r.canBubble());
@@ -353,11 +373,12 @@
public void testFlagBubble_true_intentBubble() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpIntentBubble(true /* isValid */);
+ setUpIntentBubble(true /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
assertTrue(r.canBubble());
@@ -369,11 +390,12 @@
public void testFlagBubble_false_noIntentInvalidShortcut() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpShortcutBubble(false /* isValid */);
+ setUpShortcutBubble(false /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
r.setShortcutInfo(null);
mBubbleExtractor.process(r);
@@ -386,11 +408,12 @@
public void testFlagBubble_false_invalidIntentNoShortcut() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpIntentBubble(false /* isValid */);
+ setUpIntentBubble(false /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
r.setShortcutInfo(null);
mBubbleExtractor.process(r);
@@ -403,11 +426,12 @@
public void testFlagBubble_false_noIntentNoShortcut() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
// Shortcut here is for the notification not the bubble
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
@@ -419,10 +443,11 @@
public void testFlagBubble_false_noMetadata() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- NotificationRecord r = getNotificationRecord(false /* bubble */);
+ NotificationRecord r = getNotificationRecord(false /* bubble */, mUser);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
@@ -434,11 +459,12 @@
public void testFlagBubble_false_noShortcut() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpIntentBubble(true /* isValid */);
+ setUpIntentBubble(true /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
r.setShortcutInfo(null);
r.getNotification().extras.putString(Notification.EXTRA_TEMPLATE, null);
@@ -453,11 +479,12 @@
public void testFlagBubble_false_notConversation() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpIntentBubble(true /* isValid */);
+ setUpIntentBubble(true /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
r.userDemotedAppFromConvoSpace(true);
r.getNotification().extras.putString(Notification.EXTRA_TEMPLATE, null);
@@ -472,11 +499,12 @@
public void testFlagBubble_false_lowRamDevice() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(true);
- setUpIntentBubble(true /* isValid */);
+ setUpIntentBubble(true /* isValid */, mUser);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
@@ -488,12 +516,13 @@
public void testFlagBubble_false_noIntent() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(true);
- setUpIntentBubble(true /* isValid */);
+ setUpIntentBubble(true /* isValid */, mUser);
when(mPendingIntent.getIntent()).thenReturn(null);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
@@ -505,13 +534,15 @@
public void testFlagBubble_false_noActivityInfo() {
setUpBubblesEnabled(true /* feature */,
BUBBLE_PREFERENCE_ALL /* app */,
- DEFAULT_ALLOW_BUBBLE /* channel */);
+ DEFAULT_ALLOW_BUBBLE /* channel */,
+ mUser);
when(mActivityManager.isLowRamDevice()).thenReturn(true);
- setUpIntentBubble(true /* isValid */);
+ setUpIntentBubble(true /* isValid */, mUser);
when(mPendingIntent.getIntent()).thenReturn(mIntent);
- when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(null);
+ when(mPackageManager.resolveActivityAsUser(eq(mIntent), eq(0), eq(mUser.getIdentifier())))
+ .thenReturn(null);
- NotificationRecord r = getNotificationRecord(true /* bubble */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */, mUser);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 7933f7a..1890879 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -39,6 +39,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.KeyguardManager;
import android.app.UiModeManager;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
@@ -46,6 +47,7 @@
import android.content.IntentFilter;
import android.hardware.display.ColorDisplayManager;
import android.os.PowerManager;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenDeviceEffects;
import android.testing.TestableContext;
@@ -64,6 +66,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
@RunWith(TestParameterInjector.class)
public class DefaultDeviceEffectsApplierTest {
@@ -74,6 +79,7 @@
private DefaultDeviceEffectsApplier mApplier;
@Mock PowerManager mPowerManager;
@Mock ColorDisplayManager mColorDisplayManager;
+ @Mock KeyguardManager mKeyguardManager;
@Mock UiModeManager mUiModeManager;
@Mock WallpaperManager mWallpaperManager;
@@ -83,12 +89,15 @@
mContext = spy(new TestableContext(InstrumentationRegistry.getContext(), null));
mContext.addMockSystemService(PowerManager.class, mPowerManager);
mContext.addMockSystemService(ColorDisplayManager.class, mColorDisplayManager);
+ mContext.addMockSystemService(KeyguardManager.class, mKeyguardManager);
mContext.addMockSystemService(UiModeManager.class, mUiModeManager);
mContext.addMockSystemService(WallpaperManager.class, mWallpaperManager);
when(mWallpaperManager.isWallpaperSupported()).thenReturn(true);
mApplier = new DefaultDeviceEffectsApplier(mContext);
verify(mWallpaperManager).isWallpaperSupported();
+
+ ZenLog.clear();
}
@Test
@@ -110,6 +119,41 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void apply_logsToZenLog() {
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ ArgumentCaptor<IntentFilter> intentFilterCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+
+ ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(true)
+ .build();
+ mApplier.apply(effects, ORIGIN_APP);
+
+ String zenLog = getZenLog();
+ assertThat(zenLog).contains("apply_device_effect: displayGrayscale -> true");
+ assertThat(zenLog).contains("schedule_device_effect: nightMode -> true");
+ assertThat(zenLog).doesNotContain("apply_device_effect: nightMode");
+
+ verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(),
+ intentFilterCaptor.capture(), anyInt());
+ BroadcastReceiver screenOffReceiver = broadcastReceiverCaptor.getValue();
+ screenOffReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+
+ zenLog = getZenLog();
+ assertThat(zenLog).contains("apply_device_effect: nightMode -> true");
+ }
+
+ private static String getZenLog() {
+ StringWriter zenLogWriter = new StringWriter();
+ ZenLog.dump(new PrintWriter(zenLogWriter), "");
+ return zenLogWriter.toString();
+ }
+
+ @Test
public void apply_removesEffects() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
@@ -270,6 +314,22 @@
}
@Test
+ @EnableFlags({android.app.Flags.FLAG_MODES_API, android.app.Flags.FLAG_MODES_UI})
+ public void apply_nightModeWithScreenOnAndKeyguardShowing_appliedImmediately(
+ @TestParameter ZenChangeOrigin origin) {
+
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+
+ mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(),
+ origin.value());
+
+ // Effect was applied, and no broadcast receiver was registered.
+ verify(mUiModeManager).setAttentionModeThemeOverlay(eq(MODE_ATTENTION_THEME_OVERLAY_NIGHT));
+ verify(mContext, never()).registerReceiver(any(), any(), anyInt());
+ }
+
+ @Test
@TestParameters({"{origin: ORIGIN_USER_IN_SYSTEMUI}", "{origin: ORIGIN_USER_IN_APP}",
"{origin: ORIGIN_INIT}", "{origin: ORIGIN_INIT_USER}"})
public void apply_nightModeWithScreenOn_appliedImmediatelyBasedOnOrigin(
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 fb82b872c..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,
@@ -1542,6 +1484,7 @@
assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c")));
}
+ @SuppressWarnings("GuardedBy")
@Test
public void populateComponentsToBind() {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
@@ -1565,7 +1508,8 @@
SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
- service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+ service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser,
+ /* isVisibleBackgroundUser= */ false);
assertEquals(2, componentsToBind.size());
assertEquals(1, componentsToBind.get(0).size());
@@ -1575,6 +1519,33 @@
assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void populateComponentsToBind_isVisibleBackgroundUser_addComponentsToBindButNotAddToEnabledComponent() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+ ArraySet<ComponentName> allowed = new ArraySet<>();
+ allowed.add(ComponentName.unflattenFromString("pkg1/cmp1"));
+ approvedComponentsByUser.put(11, allowed);
+ IntArray users = new IntArray();
+ users.add(11);
+
+ SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+
+ service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser,
+ /* isVisibleBackgroundUser= */ true);
+
+ assertEquals(1, componentsToBind.size());
+ assertEquals(1, componentsToBind.get(11).size());
+ assertTrue(componentsToBind.get(11).contains(ComponentName.unflattenFromString(
+ "pkg1/cmp1")));
+ assertThat(service.isComponentEnabledForCurrentProfiles(
+ new ComponentName("pkg1", "cmp1"))).isFalse();
+ assertThat(service.isComponentEnabledForPackage("pkg1")).isFalse();
+ }
+
@Test
public void testOnNullBinding() throws Exception {
Context context = mock(Context.class);
@@ -1973,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);
@@ -2018,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);
@@ -2057,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);
@@ -2128,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);
@@ -2144,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/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0a52238..196bc47 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1255,7 +1255,7 @@
info.resizeMode = RESIZE_MODE_RESIZEABLE;
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = info;
- when(mPackageManagerClient.resolveActivity(any(), anyInt())).thenReturn(ri);
+ when(mPackageManagerClient.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn(ri);
return new Notification.BubbleMetadata.Builder(
mActivityIntent,
@@ -14995,7 +14995,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_acceptsCorrectToken() throws RemoteException {
Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15014,7 +15013,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_acceptsNullToken_andPopulatesIt() throws RemoteException {
Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15031,7 +15029,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_directlyThroughRunnable_populatesAllowlistToken() {
Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15054,7 +15051,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_rejectsOtherToken() throws RemoteException {
Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15072,7 +15068,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_customParcelingWithFakeInnerToken_hasCorrectTokenInIntents()
throws RemoteException {
Notification sentFromApp = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -15278,7 +15273,6 @@
@Test
@SuppressWarnings("unchecked")
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void getActiveNotifications_doesNotLeakAllowlistToken() throws RemoteException {
Notification sentFromApp = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index f572e7a..50a5f65 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -62,6 +62,8 @@
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
+import android.media.RingtoneManager;
+import android.media.Utils;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Build;
@@ -69,6 +71,7 @@
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.VibratorInfo;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -93,6 +96,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
import java.util.ArrayList;
@SmallTest
@@ -141,6 +147,7 @@
when(mMockContext.getSystemService(eq(Vibrator.class))).thenReturn(mVibrator);
when(mVibrator.areVibrationFeaturesSupported(any())).thenReturn(true);
+ when(mVibrator.getInfo()).thenReturn(VibratorInfo.EMPTY_VIBRATOR_INFO);
final Resources res = mContext.getResources();
when(mMockContext.getResources()).thenReturn(res);
when(mMockContext.getPackageManager()).thenReturn(mPm);
@@ -511,6 +518,51 @@
}
@Test
+ @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI)
+ public void testVibration_customVibrationForSound_withoutVibrationUri() {
+ // prepare testing data
+ Uri backupDefaultUri = RingtoneManager.getActualDefaultRingtoneUri(mMockContext,
+ RingtoneManager.TYPE_NOTIFICATION);
+ RingtoneManager.setActualDefaultRingtoneUri(mMockContext, RingtoneManager.TYPE_NOTIFICATION,
+ Settings.System.DEFAULT_NOTIFICATION_URI);
+ defaultChannel.enableVibration(true);
+ defaultChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, CUSTOM_ATTRIBUTES);
+ StatusBarNotification sbn = getNotification(
+ /* channelVibrationPattern= */ null,
+ /* channelVibrationEffect= */ null,
+ /* insistent= */ false);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+
+ try {
+ assertEquals(
+ new VibratorHelper(mMockContext).createDefaultVibration(false),
+ record.getVibration());
+ } finally {
+ // restore the data
+ RingtoneManager.setActualDefaultRingtoneUri(mMockContext,
+ RingtoneManager.TYPE_NOTIFICATION,
+ backupDefaultUri);
+ }
+ }
+
+ @Test
+ @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI)
+ public void testVibration_customVibrationForSound_withVibrationUri() throws IOException {
+ defaultChannel.enableVibration(true);
+ VibrationInfo vibration = getTestingVibration(mVibrator);
+ Uri uriWithVibration = getVibrationUriAppended(
+ Settings.System.DEFAULT_NOTIFICATION_URI, vibration.mUri);
+ defaultChannel.setSound(uriWithVibration, CUSTOM_ATTRIBUTES);
+ StatusBarNotification sbn = getNotification(
+ /* channelVibrationPattern= */ null,
+ /* channelVibrationEffect= */ null,
+ /* insistent= */ false);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+
+ assertEquals(vibration.mVibrationEffect, record.getVibration());
+ }
+
+ @Test
public void testImportance_preUpgrade() {
StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
@@ -1594,4 +1646,39 @@
assertThat(record.getAudioAttributes().getUsage()).isEqualTo(USAGE_ALARM);
}
+
+ static class VibrationInfo {
+ public VibrationEffect mVibrationEffect;
+ public Uri mUri;
+ VibrationInfo(VibrationEffect vibrationEffect, Uri uri) {
+ mVibrationEffect = vibrationEffect;
+ mUri = uri;
+ }
+ }
+
+ private static VibrationInfo getTestingVibration(Vibrator vibrator) throws IOException {
+ File tempVibrationFile = File.createTempFile("test_vibration_file", ".xml");
+ FileWriter writer = new FileWriter(tempVibrationFile);
+ writer.write("<vibration-effect>\n"
+ + " <waveform-effect>\n"
+ + " <!-- PRIMING -->\n"
+ + " <waveform-entry durationMs=\"0\" amplitude=\"0\"/>\n"
+ + " <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
+ + " <waveform-entry durationMs=\"250\" amplitude=\"0\"/>\n"
+ + " <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
+ + " <waveform-entry durationMs=\"500\" amplitude=\"0\"/>\n"
+ + " </waveform-effect>\n"
+ + "</vibration-effect>"); // Your test XML content
+ writer.close();
+ Uri vibrationUri = Uri.parse(tempVibrationFile.toURI().toString());
+
+ VibrationEffect vibrationEffect = Utils.parseVibrationEffect(vibrator, vibrationUri);
+ return new VibrationInfo(vibrationEffect, vibrationUri);
+ }
+
+ private static Uri getVibrationUriAppended(Uri audioUri, Uri vibrationUri) {
+ Uri.Builder builder = audioUri.buildUpon();
+ builder.appendQueryParameter(Utils.VIBRATION_URI_PARAM, vibrationUri.toString());
+ return builder.build();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
index 0993bec..4d2396c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
@@ -22,8 +22,12 @@
import static org.mockito.Mockito.when;
+import android.media.Utils;
+import android.net.Uri;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.VibratorInfo;
+import android.provider.Settings;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -36,6 +40,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class VibratorHelperTest extends UiServiceTestCase {
@@ -86,7 +94,34 @@
}
@Test
+ public void createVibrationEffectFromSoundUri_nullInput() {
+ assertNull(mVibratorHelper.createVibrationEffectFromSoundUri(null));
+ }
+
+ @Test
+ public void createVibrationEffectFromSoundUri_emptyUri() {
+ assertNull(mVibratorHelper.createVibrationEffectFromSoundUri(Uri.EMPTY));
+ }
+
+ @Test
+ public void createVibrationEffectFromSoundUri_uriWithoutRequiredQueryParameter() {
+ Uri uri = Settings.System.DEFAULT_NOTIFICATION_URI;
+ assertNull(mVibratorHelper.createVibrationEffectFromSoundUri(uri));
+ }
+
+ @Test
+ public void createVibrationEffectFromSoundUri_uriWithVibrationUri() throws IOException {
+ // prepare the uri with vibration
+ when(mVibrator.getInfo()).thenReturn(VibratorInfo.EMPTY_VIBRATOR_INFO);
+ Uri validUri = getVibrationUriAppended(Settings.System.DEFAULT_NOTIFICATION_URI);
+
+ assertSingleVibration(mVibratorHelper.createVibrationEffectFromSoundUri(validUri));
+ }
+
+ @Test
public void createVibration_insistent_createsRepeatingVibration() {
+ when(mVibrator.getInfo()).thenReturn(VibratorInfo.EMPTY_VIBRATOR_INFO);
+
when(mVibrator.hasFrequencyControl()).thenReturn(false);
assertRepeatingVibration(mVibratorHelper.createDefaultVibration(/* insistent= */ true));
assertRepeatingVibration(mVibratorHelper.createFallbackVibration(/* insistent= */ true));
@@ -98,6 +133,8 @@
@Test
public void createVibration_nonInsistent_createsSingleShotVibration() {
+ when(mVibrator.getInfo()).thenReturn(VibratorInfo.EMPTY_VIBRATOR_INFO);
+
when(mVibrator.hasFrequencyControl()).thenReturn(false);
assertSingleVibration(mVibratorHelper.createDefaultVibration(/* insistent= */ false));
assertSingleVibration(mVibratorHelper.createFallbackVibration(/* insistent= */ false));
@@ -120,4 +157,26 @@
effect instanceof VibrationEffect.Composed);
return ((VibrationEffect.Composed) effect).getRepeatIndex();
}
+
+ private static Uri getVibrationUriAppended(Uri baseUri) throws IOException {
+ File tempVibrationFile = File.createTempFile("test_vibration_file", ".xml");
+ FileWriter writer = new FileWriter(tempVibrationFile);
+ writer.write("<vibration-effect>\n"
+ + " <waveform-effect>\n"
+ + " <!-- PRIMING -->\n"
+ + " <waveform-entry durationMs=\"0\" amplitude=\"0\"/>\n"
+ + " <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
+ + " <waveform-entry durationMs=\"250\" amplitude=\"0\"/>\n"
+ + " <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
+ + " <waveform-entry durationMs=\"500\" amplitude=\"0\"/>\n"
+ + " </waveform-effect>\n"
+ + "</vibration-effect>"); // Your test XML content
+ writer.close();
+
+ Uri.Builder builder = baseUri.buildUpon();
+ builder.appendQueryParameter(
+ Utils.VIBRATION_URI_PARAM,
+ tempVibrationFile.toURI().toString());
+ return builder.build();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index e70ed5f..f8ff1f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -788,6 +788,19 @@
}
@Test
+ public void testRuleXml_invalidInterruptionFilter_readsDefault() throws Exception {
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.zenMode = 1979;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeRuleXml(rule, baos);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+ assertThat(fromXml.zenMode).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ }
+
+ @Test
public void testZenPolicyXml_allUnset() throws Exception {
ZenPolicy policy = new ZenPolicy.Builder().build();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index c1e3f47..baa633f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -18,6 +18,8 @@
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
import static android.app.Flags.FLAG_MODES_API;
import static android.app.Flags.FLAG_MODES_UI;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
@@ -6875,6 +6877,52 @@
"Didn't find rule with id %s", ruleId);
}
+ @Test
+ @DisableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void testDefaultConfig_preModesApi_rulesAreBare() {
+ // Create a new user, which should get a copy of the default policy.
+ mZenModeHelper.onUserSwitched(101);
+
+ ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+ ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+ assertThat(eventsRule).isNotNull();
+ assertThat(eventsRule.zenPolicy).isNull();
+ assertThat(eventsRule.type).isEqualTo(TYPE_UNKNOWN);
+ assertThat(eventsRule.triggerDescription).isNull();
+ }
+
+ @Test
+ @EnableFlags(FLAG_MODES_API)
+ @DisableFlags(FLAG_MODES_UI)
+ public void testDefaultConfig_modesApi_rulesHaveFullPolicy() {
+ // Create a new user, which should get a copy of the default policy.
+ mZenModeHelper.onUserSwitched(201);
+
+ ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+ ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+ assertThat(eventsRule).isNotNull();
+ assertThat(eventsRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
+ assertThat(eventsRule.type).isEqualTo(TYPE_UNKNOWN);
+ assertThat(eventsRule.triggerDescription).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void testDefaultConfig_modesUi_rulesHaveFullPolicy() {
+ // Create a new user, which should get a copy of the default policy.
+ mZenModeHelper.onUserSwitched(301);
+
+ ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+ ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+ assertThat(eventsRule).isNotNull();
+ assertThat(eventsRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
+ assertThat(eventsRule.type).isEqualTo(TYPE_SCHEDULE_CALENDAR);
+ assertThat(eventsRule.triggerDescription).isNotEmpty();
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 031d1c2..946e1ea 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -68,6 +68,9 @@
private int[] mSupportedPrimitives;
private int mCompositionSizeMax;
private int mPwleSizeMax;
+ private int mMaxEnvelopeEffectSize;
+ private int mMinEnvelopeEffectControlPointDurationMillis;
+ private int mMaxEnvelopeEffectControlPointDurationMillis;
private float mMinFrequency = Float.NaN;
private float mResonantFrequency = Float.NaN;
private float mFrequencyResolution = Float.NaN;
@@ -217,6 +220,11 @@
infoBuilder.setQFactor(mQFactor);
infoBuilder.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes));
+ infoBuilder.setMaxEnvelopeEffectSize(mMaxEnvelopeEffectSize);
+ infoBuilder.setMinEnvelopeEffectControlPointDurationMillis(
+ mMinEnvelopeEffectControlPointDurationMillis);
+ infoBuilder.setMaxEnvelopeEffectControlPointDurationMillis(
+ mMaxEnvelopeEffectControlPointDurationMillis);
return mIsInfoLoadSuccessful;
}
@@ -358,6 +366,26 @@
}
/**
+ * Set the maximum number of envelope effects control points supported in fake vibrator
+ * hardware.
+ */
+ public void setMaxEnvelopeEffectSize(int envelopeEffectControlPointsMax) {
+ mMaxEnvelopeEffectSize = envelopeEffectControlPointsMax;
+ }
+
+ /** Set the envelope effect minimum segment duration in fake vibrator hardware. */
+ public void setMinEnvelopeEffectControlPointDurationMillis(
+ int minEnvelopeEffectControlPointDurationMillis) {
+ mMinEnvelopeEffectControlPointDurationMillis = minEnvelopeEffectControlPointDurationMillis;
+ }
+
+ /** Set the envelope effect maximum segment duration in fake vibrator hardware. */
+ public void setMaxEnvelopeEffectControlPointDurationMillis(
+ int maxEnvelopeEffectControlPointDurationMillis) {
+ mMaxEnvelopeEffectControlPointDurationMillis = maxEnvelopeEffectControlPointDurationMillis;
+ }
+
+ /**
* Return the amplitudes set by this controller, including zeroes for each time the vibrator was
* turned off.
*/
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index fef5d4c..4e59fe5 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -109,3 +109,35 @@
":OverlayTestApp",
],
}
+
+test_module_config {
+ name: "WmTests_server_policy_Presubmit",
+ base: "WmTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.policy."],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+ name: "WmTests_server_policy",
+ base: "WmTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.policy."],
+}
+
+test_module_config {
+ name: "WmTests_wm_utils_Presubmit",
+ base: "WmTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["com.android.server.wm.utils"],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index d147325..0575d98 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -41,6 +41,8 @@
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -50,6 +52,8 @@
import com.android.internal.R;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Collections;
@@ -62,7 +66,13 @@
*/
@SmallTest
+@EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR)
public class ModifierShortcutManagerTests {
+
+ @ClassRule public static final SetFlagsRule.ClassRule SET_FLAGS_CLASS_RULE =
+ new SetFlagsRule.ClassRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = SET_FLAGS_CLASS_RULE.createSetFlagsRule();
+
private ModifierShortcutManager mModifierShortcutManager;
private Handler mHandler;
private Context mContext;
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 71f90a2..43171f8 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -44,26 +44,21 @@
import android.content.ComponentName;
import android.content.Intent;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
@Presubmit
@SmallTest
+@EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR)
public class ModifierShortcutTests extends ShortcutKeyTestBase {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
private static final SparseArray<String> INTENT_SHORTCUTS = new SparseArray<>();
private static final SparseArray<String> ROLE_SHORTCUTS = new SparseArray<>();
static {
@@ -258,7 +253,7 @@
* META+CTRL+BACKSPACE for taking a bugreport when the flag is enabled.
*/
@Test
- @RequiresFlagsEnabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+ @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
public void testTakeBugReport_flagEnabled() throws RemoteException {
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0);
mPhoneWindowManager.assertTakeBugreport(true);
@@ -268,7 +263,7 @@
* META+CTRL+BACKSPACE for taking a bugreport does nothing when the flag is disabledd.
*/
@Test
- @RequiresFlagsDisabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+ @DisableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
public void testTakeBugReport_flagDisabled() throws RemoteException {
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0);
mPhoneWindowManager.assertTakeBugreport(false);
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 07934ea..536dcfb 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -42,7 +42,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -135,15 +134,13 @@
doNothing().when(mPhoneWindowManager).initializeHdmiState();
final boolean[] isScreenTurnedOff = { false };
final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
- doAnswer(invocation -> isScreenTurnedOff[0] = true).when(displayPolicy).screenTurnedOff();
+ doAnswer(invocation -> isScreenTurnedOff[0] = true).when(displayPolicy).screenTurnedOff(
+ anyBoolean());
doAnswer(invocation -> !isScreenTurnedOff[0]).when(displayPolicy).isScreenOnEarly();
doAnswer(invocation -> !isScreenTurnedOff[0]).when(displayPolicy).isScreenOnFully();
mPhoneWindowManager.mDefaultDisplayPolicy = displayPolicy;
mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
- final ActivityTaskManagerInternal.SleepTokenAcquirer tokenAcquirer =
- mock(ActivityTaskManagerInternal.SleepTokenAcquirer.class);
- doReturn(tokenAcquirer).when(mAtmInternal).createSleepTokenAcquirer(anyString());
final PowerManager pm = mock(PowerManager.class);
doReturn(true).when(pm).isInteractive();
doReturn(pm).when(mContext).getSystemService(eq(Context.POWER_SERVICE));
@@ -155,9 +152,8 @@
assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isFalse();
// Skip sleep-token for non-sleep-screen-off.
- clearInvocations(tokenAcquirer);
mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
- verify(tokenAcquirer, never()).acquire(anyInt());
+ verify(displayPolicy).screenTurnedOff(false /* acquireSleepToken */);
assertThat(isScreenTurnedOff[0]).isTrue();
// Apply sleep-token for sleep-screen-off.
@@ -165,21 +161,10 @@
mPhoneWindowManager.startedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isTrue();
mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
- verify(tokenAcquirer).acquire(eq(DEFAULT_DISPLAY));
+ verify(displayPolicy).screenTurnedOff(true /* acquireSleepToken */);
mPhoneWindowManager.finishedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isFalse();
-
- // Simulate unexpected reversed order: screenTurnedOff -> startedGoingToSleep. The sleep
- // token can still be acquired.
- isScreenTurnedOff[0] = false;
- clearInvocations(tokenAcquirer);
- mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
- verify(tokenAcquirer, never()).acquire(anyInt());
- assertThat(displayPolicy.isScreenOnEarly()).isFalse();
- assertThat(displayPolicy.isScreenOnFully()).isFalse();
- mPhoneWindowManager.startedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
- verify(tokenAcquirer).acquire(eq(DEFAULT_DISPLAY));
}
@Test
@@ -188,7 +173,7 @@
.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER,
- /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+ /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
}
@@ -198,7 +183,7 @@
.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
- /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+ /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY);
}
@@ -208,7 +193,7 @@
.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
- /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+ /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 1e035da..e2e76d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3231,7 +3231,7 @@
mDisplayContent.mOpeningApps.remove(activity);
mDisplayContent.mClosingApps.remove(activity);
activity.commitVisibility(false /* visible */, false /* performLayout */);
- mDisplayContent.getDisplayPolicy().screenTurnedOff();
+ mDisplayContent.getDisplayPolicy().screenTurnedOff(false /* acquireSleepToken */);
final KeyguardController controller = mSupervisor.getKeyguardController();
doReturn(true).when(controller).isKeyguardGoingAway(anyInt());
activity.setVisibility(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 4f94704..c706d52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -1047,6 +1047,7 @@
info.packageName = packageName;
WindowProcessController wpc = new WindowProcessController(
mAtm, info, packageName, 0, userId, null, mMockListener);
+ mAtm.mInternal.preBindApplication(wpc, info);
wpc.setThread(mock(IApplicationThread.class));
return wpc;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index a7a08b2..8227ed91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -139,11 +139,6 @@
/* isUnresizable */ true);
}
- void activateCameraInPolicy(boolean isCameraActive) {
- doReturn(isCameraActive).when(mDisplayContent.mAppCompatCameraPolicy)
- .isCameraActive(any(ActivityRecord.class), anyBoolean());
- }
-
void setDisplayNaturalOrientation(@Configuration.Orientation int naturalOrientation) {
doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index ba2a733..d66c21a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -267,7 +267,6 @@
robot.conf().enableCameraCompatSplitScreenAspectRatio(true);
robot.applyOnActivity((a) -> {
a.createActivityWithComponentInNewTask();
- a.activateCameraInPolicy(true);
a.setShouldCreateCompatDisplayInsets(false);
});
@@ -276,27 +275,12 @@
}
@Test
- public void testIsCameraActive() {
- runTestScenario((robot) -> {
- robot.applyOnActivity((a) -> {
- a.createActivityWithComponent();
- a.activateCameraInPolicy(/* isCameraActive */ false);
- robot.checkIsCameraActive(/* active */ false);
- a.activateCameraInPolicy(/* isCameraActive */ true);
- robot.checkIsCameraActive(/* active */ true);
- });
- });
- }
-
-
- @Test
@EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponent();
- robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
- robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ true);
+ robot.checkIsOverrideMinAspectRatioForCameraEnabled(/* expected */ true);
});
}
@@ -306,21 +290,8 @@
runTestScenario((robot) -> {
robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
robot.activity().createActivityWithComponent();
- robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
- robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ true);
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsFalse() {
- runTestScenario((robot) -> {
- robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
- robot.activity().createActivityWithComponent();
- robot.activity().activateCameraInPolicy(/* isCameraActive */ false);
-
- robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ robot.checkIsOverrideMinAspectRatioForCameraEnabled(/* expected */ true);
});
}
@@ -330,9 +301,8 @@
runTestScenario((robot) -> {
robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
robot.activity().createActivityWithComponent();
- robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
- robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ robot.checkIsOverrideMinAspectRatioForCameraEnabled(/* expected */ false);
});
}
@@ -341,9 +311,8 @@
public void shouldOverrideMinAspectRatioForCamera_overrideDisabled_returnsFalse() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponent();
- robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
- robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ robot.checkIsOverrideMinAspectRatioForCameraEnabled(/* expected */ false);
});
}
@@ -354,7 +323,7 @@
robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
robot.activity().createActivityWithComponent();
- robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ robot.checkIsOverrideMinAspectRatioForCameraEnabled(/* expected */ false);
});
}
@@ -364,9 +333,8 @@
runTestScenario((robot) -> {
robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
robot.activity().createActivityWithComponent();
- robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
- robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ robot.checkIsOverrideMinAspectRatioForCameraEnabled(/* expected */ false);
});
}
@@ -412,13 +380,9 @@
.shouldApplyFreeformTreatmentForCameraCompat(), expected);
}
- void checkShouldOverrideMinAspectRatioForCamera(boolean expected) {
+ void checkIsOverrideMinAspectRatioForCameraEnabled(boolean expected) {
Assert.assertEquals(getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera(), expected);
- }
-
- void checkIsCameraActive(boolean active) {
- Assert.assertEquals(getAppCompatCameraOverrides().isCameraActive(), active);
+ .isOverrideMinAspectRatioForCameraEnabled(), expected);
}
private AppCompatCameraOverrides getAppCompatCameraOverrides() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
index 2ae23f8..d91b38e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
@@ -31,6 +33,9 @@
import androidx.annotation.NonNull;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -186,16 +191,6 @@
});
}
- /**
- * Runs a test scenario providing a Robot.
- */
- void runTestScenario(@NonNull Consumer<AppCompatCameraPolicyRobotTest> consumer) {
- spyOn(mWm.mAppCompatConfiguration);
- final AppCompatCameraPolicyRobotTest robot =
- new AppCompatCameraPolicyRobotTest(mWm, mAtm, mSupervisor);
- consumer.accept(robot);
- }
-
@Test
public void testIsCameraCompatTreatmentActive_whenTreatmentForTopActivityIsEnabled() {
runTestScenario((robot) -> {
@@ -220,6 +215,58 @@
});
}
+ @Test
+ @EnableCompatChanges(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA)
+ public void testShouldOverrideMinAspectRatioForCamera_whenCameraIsNotRunning() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a)-> {
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+ a.createActivityWithComponentInNewTaskAndDisplay();
+ a.setTopActivityCameraActive(/* active */ false);
+ });
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* active */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA)
+ public void testShouldOverrideMinAspectRatioForCamera_whenCameraIsRunning_overrideDisabled() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a)-> {
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+ a.createActivityWithComponentInNewTaskAndDisplay();
+ a.setTopActivityCameraActive(/* active */ true);
+ });
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* active */ false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA)
+ public void testShouldOverrideMinAspectRatioForCamera_whenCameraIsRunning_overrideEnabled() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a)-> {
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+ a.createActivityWithComponentInNewTaskAndDisplay();
+ a.setTopActivityCameraActive(/* active */ true);
+ });
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* active */ true);
+ });
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ void runTestScenario(@NonNull Consumer<AppCompatCameraPolicyRobotTest> consumer) {
+ final AppCompatCameraPolicyRobotTest robot =
+ new AppCompatCameraPolicyRobotTest(mWm, mAtm, mSupervisor);
+ consumer.accept(robot);
+ }
+
+
private static class AppCompatCameraPolicyRobotTest extends AppCompatRobotBase {
AppCompatCameraPolicyRobotTest(@NonNull WindowManagerService wm,
@NonNull ActivityTaskManagerService atm,
@@ -230,7 +277,14 @@
@Override
void onPostDisplayContentCreation(@NonNull DisplayContent displayContent) {
super.onPostDisplayContentCreation(displayContent);
+
spyOn(displayContent.mAppCompatCameraPolicy);
+ if (displayContent.mAppCompatCameraPolicy.mDisplayRotationCompatPolicy != null) {
+ spyOn(displayContent.mAppCompatCameraPolicy.mDisplayRotationCompatPolicy);
+ }
+ if (displayContent.mAppCompatCameraPolicy.mCameraCompatFreeformPolicy != null) {
+ spyOn(displayContent.mAppCompatCameraPolicy.mCameraCompatFreeformPolicy);
+ }
}
void checkTopActivityHasDisplayRotationCompatPolicy(boolean exists) {
@@ -268,6 +322,11 @@
.isTreatmentEnabledForActivity(activity().top()), active);
}
+ void checkShouldOverrideMinAspectRatioForCamera(boolean expected) {
+ assertEquals(getTopAppCompatCameraPolicy()
+ .shouldOverrideMinAspectRatioForCamera(activity().top()), expected);
+ }
+
// TODO(b/350460645): Create Desktop Windowing Robot to reuse common functionalities.
void allowEnterDesktopMode(boolean isAllowed) {
doReturn(isAllowed).when(() ->
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 af4394a..c294bc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -25,11 +25,15 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -37,6 +41,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -46,11 +51,14 @@
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.gui.DropInputMode;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -86,6 +94,7 @@
public void setUp() throws Exception {
assumeFalse(WindowManagerService.sEnableShellTransitions);
mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
+ mWm.mAnimator.ready();
}
@Test
@@ -833,6 +842,353 @@
}
@Test
+ public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord activity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(activity);
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+ waitUntilWindowAnimatorIdle();
+
+ // Animation run by the remote handler.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_noOverrideWithOnlyTaskFragmentFillingTask() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord closingActivity = createActivityRecord(task);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+
+ // Make sure the TaskFragment is not embedded.
+ assertFalse(taskFragment.isEmbeddedWithBoundsOverride());
+ final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(closingActivity);
+ prepareActivityForAppTransition(openingActivity);
+ final int uid = 12345;
+ closingActivity.info.applicationInfo.uid = uid;
+ openingActivity.info.applicationInfo.uid = uid;
+ task.effectiveUid = uid;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity,
+ null /* changingTaskFragment */);
+ waitUntilWindowAnimatorIdle();
+
+ // Animation is not run by the remote handler because the activity is filling the Task.
+ assertFalse(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_overrideWithTaskFragmentNotFillingTask() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord closingActivity = createActivityRecord(task);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+
+ // Make sure the TaskFragment is embedded.
+ taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ final Rect embeddedBounds = new Rect(task.getBounds());
+ embeddedBounds.right = embeddedBounds.left + embeddedBounds.width() / 2;
+ taskFragment.setBounds(embeddedBounds);
+ assertTrue(taskFragment.isEmbeddedWithBoundsOverride());
+ final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(closingActivity);
+ prepareActivityForAppTransition(openingActivity);
+ final int uid = 12345;
+ closingActivity.info.applicationInfo.uid = uid;
+ openingActivity.info.applicationInfo.uid = uid;
+ task.effectiveUid = uid;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity,
+ null /* changingTaskFragment */);
+ waitUntilWindowAnimatorIdle();
+
+ // Animation run by the remote handler.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Closing non-embedded activity.
+ final ActivityRecord closingActivity = createActivityRecord(task);
+ prepareActivityForAppTransition(closingActivity);
+ // Opening TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(openingActivity);
+ task.effectiveUid = openingActivity.getUid();
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+ waitUntilWindowAnimatorIdle();
+
+ // Animation run by the remote handler.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Closing TaskFragment with embedded activity.
+ final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord closingActivity = taskFragment1.getTopMostActivity();
+ prepareActivityForAppTransition(closingActivity);
+ closingActivity.info.applicationInfo.uid = 12345;
+ // Opening TaskFragment with embedded activity with different UID.
+ final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord openingActivity = taskFragment2.getTopMostActivity();
+ prepareActivityForAppTransition(openingActivity);
+ openingActivity.info.applicationInfo.uid = 54321;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
+ waitUntilWindowAnimatorIdle();
+
+ // Animation run by the remote handler.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Closing activity in Task1.
+ final ActivityRecord closingActivity = createActivityRecord(mDisplayContent);
+ prepareActivityForAppTransition(closingActivity);
+ // Opening TaskFragment with embedded activity in Task2.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(openingActivity);
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+ waitUntilWindowAnimatorIdle();
+
+ // Animation not run by the remote handler.
+ assertFalse(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Closing TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(closingActivity);
+ closingActivity.info.applicationInfo.uid = 12345;
+ task.effectiveUid = closingActivity.getUid();
+ // Opening non-embedded activity with different UID.
+ final ActivityRecord openingActivity = createActivityRecord(task);
+ prepareActivityForAppTransition(openingActivity);
+ openingActivity.info.applicationInfo.uid = 54321;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+ waitUntilWindowAnimatorIdle();
+
+ // Animation should not run by the remote handler when there are non-embedded activities of
+ // different UID.
+ assertFalse(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_noOverrideWithWallpaper() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord activity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(activity);
+ // Set wallpaper as visible.
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+ waitUntilWindowAnimatorIdle();
+
+ // Animation should not run by the remote handler when there is wallpaper in the transition.
+ assertFalse(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_inputProtectedForUntrustedAnimation() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with embedded activities, one is trusted embedded, and the other
+ // one is untrusted embedded.
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .createActivityCount(2)
+ .setOrganizer(organizer)
+ .build();
+ final ActivityRecord activity0 = taskFragment.getChildAt(0).asActivityRecord();
+ final ActivityRecord activity1 = taskFragment.getChildAt(1).asActivityRecord();
+ // Also create a non-embedded activity in the Task.
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
+ task.addChild(activity2, POSITION_BOTTOM);
+ prepareActivityForAppTransition(activity0);
+ prepareActivityForAppTransition(activity1);
+ prepareActivityForAppTransition(activity2);
+ doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0);
+ doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1);
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment);
+ waitUntilWindowAnimatorIdle();
+
+ // The animation will be animated remotely by client and all activities are input disabled
+ // for untrusted animation.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ verify(activity0).setDropInputForAnimation(true);
+ verify(activity1).setDropInputForAnimation(true);
+ verify(activity2).setDropInputForAnimation(true);
+ verify(activity0).setDropInputMode(DropInputMode.ALL);
+ verify(activity1).setDropInputMode(DropInputMode.ALL);
+ verify(activity2).setDropInputMode(DropInputMode.ALL);
+
+ // Reset input after animation is finished.
+ clearInvocations(activity0);
+ clearInvocations(activity1);
+ clearInvocations(activity2);
+ remoteAnimationRunner.finishAnimation();
+
+ verify(activity0).setDropInputForAnimation(false);
+ verify(activity1).setDropInputForAnimation(false);
+ verify(activity2).setDropInputForAnimation(false);
+ verify(activity0).setDropInputMode(DropInputMode.OBSCURED);
+ verify(activity1).setDropInputMode(DropInputMode.NONE);
+ verify(activity2).setDropInputMode(DropInputMode.NONE);
+ }
+
+ /**
+ * Since we don't have any use case to rely on handling input during animation, disable it even
+ * if it is trusted embedding so that it could cover some edge-cases when a previously trusted
+ * host starts doing something bad.
+ */
+ @Test
+ public void testOverrideTaskFragmentAdapter_inputProtectedForTrustedAnimation() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with only trusted embedded activity
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .createActivityCount(1)
+ .setOrganizer(organizer)
+ .build();
+ final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord();
+ prepareActivityForAppTransition(activity);
+ doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity);
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+ waitUntilWindowAnimatorIdle();
+
+ // The animation will be animated remotely by client and all activities are input disabled
+ // for untrusted animation.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ verify(activity).setDropInputForAnimation(true);
+ verify(activity).setDropInputMode(DropInputMode.ALL);
+
+ // Reset input after animation is finished.
+ clearInvocations(activity);
+ remoteAnimationRunner.finishAnimation();
+
+ verify(activity).setDropInputForAnimation(false);
+ verify(activity).setDropInputMode(DropInputMode.NONE);
+ }
+
+ /**
+ * We don't need to drop input for fully trusted embedding (system app, and embedding in the
+ * same app). This will allow users to do fast tapping.
+ */
+ @Test
+ public void testOverrideTaskFragmentAdapter_noInputProtectedForFullyTrustedAnimation() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with only trusted embedded activity
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .createActivityCount(1)
+ .setOrganizer(organizer)
+ .build();
+ final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord();
+ prepareActivityForAppTransition(activity);
+ final int uid = mAtm.mTaskFragmentOrganizerController.getTaskFragmentOrganizerUid(
+ getITaskFragmentOrganizer(organizer));
+ doReturn(true).when(task).isFullyTrustedEmbedding(uid);
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+ waitUntilWindowAnimatorIdle();
+
+ // The animation will be animated remotely by client, but input should not be dropped for
+ // fully trusted.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ verify(activity, never()).setDropInputForAnimation(true);
+ verify(activity, never()).setDropInputMode(DropInputMode.ALL);
+ }
+
+ @Test
public void testTransitionGoodToGoForTaskFragments() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final Task task = createTask(mDisplayContent);
@@ -898,6 +1254,22 @@
verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
}
+ /** Registers remote animation for the organizer. */
+ private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer,
+ TestRemoteAnimationRunner remoteAnimationRunner) {
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ remoteAnimationRunner, 10, 1);
+ final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer);
+ final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, adapter);
+ registerTaskFragmentOrganizer(iOrganizer);
+ mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
+ }
+
private static ITaskFragmentOrganizer getITaskFragmentOrganizer(
TaskFragmentOrganizer organizer) {
return ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
@@ -931,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/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 44837d7..20dcdde 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -110,6 +110,7 @@
mWindowManagerInternal = mock(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
mBackAnimationAdapter = mock(BackAnimationAdapter.class);
+ doReturn(true).when(mBackAnimationAdapter).isAnimatable(anyInt());
mNavigationMonitor = mock(BackNavigationController.NavigationMonitor.class);
mRootHomeTask = initHomeActivity();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 6e48818..3910904 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -18,6 +18,7 @@
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT;
@@ -25,9 +26,11 @@
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -43,6 +46,9 @@
import android.content.pm.PackageManagerInternal;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.DeviceConfig;
import android.util.Pair;
@@ -52,6 +58,7 @@
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.am.PendingIntentRecord;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
+import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -95,7 +102,9 @@
@Rule
public final ExtendedMockitoRule extendedMockitoRule =
new ExtendedMockitoRule.Builder(this).setStrictness(Strictness.LENIENT).build();
-
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
TestableBackgroundActivityStartController mController;
@Mock
ActivityMetricsLogger mActivityMetricsLogger;
@@ -186,7 +195,7 @@
when(mAppOpsManager.checkOpNoThrow(
eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
anyInt(), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
- when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
BalVerdict.BLOCK);
}
@@ -227,7 +236,7 @@
// call
BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
balState);
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForCaller(callerVerdict);
@@ -295,7 +304,77 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+ balState);
+ balState.setResultForRealCaller(realCallerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(realCallerVerdict.getCode()).isEqualTo(
+ BAL_ALLOW_VISIBLE_WINDOW);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+ public void testCaller_appHasVisibleWindowWithIfVisibleOptIn() {
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ when(mService.hasActiveVisibleWindow(eq(callingUid))).thenReturn(true);
+ when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = mCheckedOptions
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // call
+ BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
+ balState);
+ balState.setResultForCaller(callerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+ BAL_ALLOW_VISIBLE_WINDOW);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+ public void testRealCaller_appHasVisibleWindowWithIfVisibleOptIn() {
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ when(mService.hasActiveVisibleWindow(eq(realCallingUid))).thenReturn(true);
+ when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = mCheckedOptions
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // call
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
@@ -320,7 +399,7 @@
int realCallingPid = REGULAR_PID_2;
// setup state
- when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
@@ -357,7 +436,7 @@
mService.getProcessController(eq(realCallingPid), eq(realCallingUid))).thenReturn(
mCallerApp);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
- when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
// prepare call
@@ -371,7 +450,7 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
@@ -404,9 +483,9 @@
mService.getProcessController(eq(realCallingPid), eq(realCallingUid))).thenReturn(
mCallerApp);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
- when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
BalVerdict.BLOCK);
- when(otherProcess.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(otherProcess.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
// prepare call
@@ -420,7 +499,7 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
@@ -456,7 +535,7 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
@@ -466,6 +545,45 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+ public void testRealCaller_isCompanionAppWithOptInIfVisible() {
+ // The app has a service that is bound by a different, visible app. The app bound to the
+ // service must remain visible for the app in the background to start activities
+ // successfully.
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ final int realCallingUserId = UserHandle.getUserId(realCallingUid);
+ when(mService.isAssociatedCompanionApp(eq(realCallingUserId),
+ eq(realCallingUid))).thenReturn(true);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = mCheckedOptions
+ .setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, null,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // call
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+ balState);
+ balState.setResultForRealCaller(realCallerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(realCallerVerdict.getCode()).isEqualTo(
+ BAL_BLOCK);
+ }
+
+ @Test
public void testCaller_balPermission() {
int callingUid = REGULAR_UID_1;
int callingPid = REGULAR_PID_1;
@@ -523,7 +641,7 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index e364264..6ec789599 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -24,6 +24,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -43,6 +44,7 @@
import com.android.compatibility.common.util.DeviceConfigStateHelper;
import com.android.server.am.PendingIntentRecord;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
import org.junit.After;
import org.junit.Before;
@@ -167,9 +169,9 @@
}
@Override
- BalVerdict checkBackgroundActivityStartAllowedBySender(BalState state) {
+ BalVerdict checkBackgroundActivityStartAllowedByRealCaller(BalState state) {
return mRealCallerVerdict.orElseGet(
- () -> super.checkBackgroundActivityStartAllowedBySender(state));
+ () -> super.checkBackgroundActivityStartAllowedByRealCaller(state));
}
public void setRealCallerVerdict(BalVerdict verdict) {
@@ -177,11 +179,12 @@
}
@Override
- BalVerdict checkProcessAllowsBal(WindowProcessController app, BalState state) {
+ BalVerdict checkProcessAllowsBal(WindowProcessController app, BalState state,
+ BalCheckConfiguration checkConfiguration) {
if (mProcessVerdicts.containsKey(app)) {
return mProcessVerdicts.get(app);
}
- return super.checkProcessAllowsBal(app, state);
+ return super.checkProcessAllowsBal(app, state, checkConfiguration);
}
}
@@ -209,7 +212,7 @@
Mockito.when(mAppOpsManager.checkOpNoThrow(
eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
anyInt(), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
- Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
BalVerdict.BLOCK);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
index c9c7e92..27e147d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_DISALLOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_BOUND_BY_FOREGROUND;
@@ -95,7 +96,12 @@
int mUid = 234;
String mPackageName = "package.name";
int mAppSwitchState = APP_SWITCH_DISALLOW;
- boolean mIsCheckingForFgsStart = false;
+ BackgroundLaunchProcessController.BalCheckConfiguration mBalCheckConfiguration =
+ new BackgroundLaunchProcessController.BalCheckConfiguration(
+ /* isCheckingForFgsStarts */ false,
+ /* checkVisibility */ true,
+ /* checkOtherExemptions */ true,
+ ACTIVITY_BG_START_GRACE_PERIOD_MS);
boolean mHasActivityInVisibleTask = false;
boolean mHasBackgroundActivityStartPrivileges = false;
long mLastStopAppSwitchesTime = 0L;
@@ -106,7 +112,7 @@
public void testNothingAllows() {
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -118,7 +124,7 @@
mHasBackgroundActivityStartPrivileges = true;
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -136,7 +142,7 @@
BackgroundStartPrivileges.ALLOW_BAL);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -154,7 +160,7 @@
BackgroundStartPrivileges.ALLOW_BAL);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -170,7 +176,7 @@
BackgroundStartPrivileges.ALLOW_BAL);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -186,7 +192,7 @@
BackgroundStartPrivileges.ALLOW_BAL);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -201,7 +207,7 @@
mHasActiveVisibleWindow.add(999);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -216,7 +222,7 @@
mHasActiveVisibleWindow.add(999);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -229,7 +235,7 @@
mHasActivityInVisibleTask = true;
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -245,7 +251,7 @@
mLastActivityFinishTime = now - 100;
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index e57e36d..1f3aa35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -52,39 +52,14 @@
@RunWith(WindowTestRunner.class)
public class DimmerTests extends WindowTestsBase {
- private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
- final SurfaceControl mControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction mPendingTransaction = spy(StubTransaction.class);
- final SurfaceControl.Transaction mSyncTransaction = spy(StubTransaction.class);
-
- TestWindowContainer(WindowManagerService wm) {
- super(wm);
- setVisibleRequested(true);
- }
-
- @Override
- public SurfaceControl getSurfaceControl() {
- return mControl;
- }
-
- @Override
- public SurfaceControl.Transaction getSyncTransaction() {
- return mSyncTransaction;
- }
-
- @Override
- public SurfaceControl.Transaction getPendingTransaction() {
- return mPendingTransaction;
- }
- }
-
- private static class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
+ private static class MockSurfaceBuildingContainer extends WindowContainer<WindowState> {
final SurfaceSession mSession = new SurfaceSession();
final SurfaceControl mHostControl = mock(SurfaceControl.class);
final SurfaceControl.Transaction mHostTransaction = spy(StubTransaction.class);
MockSurfaceBuildingContainer(WindowManagerService wm) {
super(wm);
+ mVisibleRequested = true;
}
class MockSurfaceBuilder extends SurfaceControl.Builder {
@@ -129,28 +104,41 @@
}
}
- private MockSurfaceBuildingContainer mHost;
private Dimmer mDimmer;
private SurfaceControl.Transaction mTransaction;
- private TestWindowContainer mChild;
+ private WindowState mChild1;
+ private WindowState mChild2;
private static AnimationAdapter sTestAnimation;
@Before
public void setUp() throws Exception {
- mHost = new MockSurfaceBuildingContainer(mWm);
- mTransaction = spy(StubTransaction.class);
- mChild = new TestWindowContainer(mWm);
+ MockSurfaceBuildingContainer host = new MockSurfaceBuildingContainer(mWm);
+ mTransaction = host.getSyncTransaction();
+
+ final SurfaceControl mControl1 = mock(SurfaceControl.class);
+ final SurfaceControl mControl2 = mock(SurfaceControl.class);
+
+ SurfaceAnimator animator = mock(SurfaceAnimator.class);
+ when(animator.getAnimation()).thenReturn(null);
+
+ mChild1 = mock(WindowState.class);
+ when(mChild1.getSurfaceControl()).thenReturn(mControl1);
+
+ mChild2 = mock(WindowState.class);
+ when(mChild2.getSurfaceControl()).thenReturn(mControl2);
+
+ host.addChild(mChild1, 0);
+ host.addChild(mChild2, 1);
+
sTestAnimation = spy(new MockAnimationAdapter());
- mDimmer = new Dimmer(mHost, new MockAnimationAdapterFactory());
+ mDimmer = new Dimmer(host, new MockAnimationAdapterFactory());
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
public void testUpdateDimsAppliesCrop() {
- mHost.addChild(mChild, 0);
-
- mDimmer.adjustAppearance(mChild, 1, 1);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, 1, 1);
+ mDimmer.adjustPosition(mChild1, mChild1);
int width = 100;
int height = 300;
@@ -165,9 +153,8 @@
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() {
final float alpha = 0.7f;
final int blur = 50;
- mHost.addChild(mChild, 0);
- mDimmer.adjustAppearance(mChild, alpha, blur);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, alpha, blur);
+ mDimmer.adjustPosition(mChild1, mChild1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
@@ -175,25 +162,23 @@
mDimmer.updateDims(mTransaction);
verify(sTestAnimation).startAnimation(eq(dimLayer), eq(mTransaction),
anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
- verify(mTransaction).setRelativeLayer(dimLayer, mChild.mControl, -1);
+ verify(mTransaction).setRelativeLayer(dimLayer, mChild1.getSurfaceControl(), -1);
verify(mTransaction, lastCall()).setAlpha(dimLayer, alpha);
verify(mTransaction).setBackgroundBlurRadius(dimLayer, blur);
}
@Test
public void testDimBelowWithChildSurfaceDestroyedWhenReset() {
- mHost.addChild(mChild, 0);
-
final float alpha = 0.8f;
final int blur = 50;
// Dim once
- mDimmer.adjustAppearance(mChild, alpha, blur);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, alpha, blur);
+ mDimmer.adjustPosition(mChild1, mChild1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
// Reset, and don't dim
mDimmer.resetDimStates();
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustPosition(mChild1, mChild1);
mDimmer.updateDims(mTransaction);
verify(mTransaction).show(dimLayer);
verify(mTransaction).remove(dimLayer);
@@ -201,19 +186,17 @@
@Test
public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() {
- mHost.addChild(mChild, 0);
-
final float alpha = 0.8f;
final int blur = 20;
// Dim once
- mDimmer.adjustAppearance(mChild, alpha, blur);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, alpha, blur);
+ mDimmer.adjustPosition(mChild1, mChild1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
// Reset and dim again
mDimmer.resetDimStates();
- mDimmer.adjustAppearance(mChild, alpha, blur);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, alpha, blur);
+ mDimmer.adjustPosition(mChild1, mChild1);
mDimmer.updateDims(mTransaction);
verify(mTransaction).show(dimLayer);
verify(mTransaction, never()).remove(dimLayer);
@@ -222,10 +205,9 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
public void testDimUpdateWhileDimming() {
- mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.adjustAppearance(mChild, alpha, 20);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, alpha, 20);
+ mDimmer.adjustPosition(mChild1, mChild1);
final Rect bounds = mDimmer.getDimBounds();
SurfaceControl dimLayer = mDimmer.getDimLayer();
@@ -243,9 +225,8 @@
@Test
public void testRemoveDimImmediately() {
- mHost.addChild(mChild, 0);
- mDimmer.adjustAppearance(mChild, 1, 2);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, 1, 2);
+ mDimmer.adjustPosition(mChild1, mChild1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
verify(mTransaction, times(1)).show(dimLayer);
@@ -266,22 +247,20 @@
*/
@Test
public void testContainerDimsOpeningAnimationByItself() {
- mHost.addChild(mChild, 0);
-
mDimmer.resetDimStates();
- mDimmer.adjustAppearance(mChild, 0.1f, 0);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, 0.1f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
mDimmer.resetDimStates();
- mDimmer.adjustAppearance(mChild, 0.2f, 0);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, 0.2f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
mDimmer.updateDims(mTransaction);
mDimmer.resetDimStates();
- mDimmer.adjustAppearance(mChild, 0.3f, 0);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, 0.3f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
mDimmer.updateDims(mTransaction);
verify(mTransaction).setAlpha(dimLayer, 0.2f);
@@ -297,22 +276,20 @@
*/
@Test
public void testContainerDimsClosingAnimationByItself() {
- mHost.addChild(mChild, 0);
-
mDimmer.resetDimStates();
- mDimmer.adjustAppearance(mChild, 0.2f, 0);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, 0.2f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
mDimmer.resetDimStates();
- mDimmer.adjustAppearance(mChild, 0.1f, 0);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, 0.1f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
mDimmer.updateDims(mTransaction);
mDimmer.resetDimStates();
- mDimmer.adjustAppearance(mChild, 0f, 0);
- mDimmer.adjustPosition(mChild, mChild, -1);
+ mDimmer.adjustAppearance(mChild1, 0f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
mDimmer.updateDims(mTransaction);
mDimmer.resetDimStates();
@@ -325,19 +302,14 @@
*/
@Test
public void testMultipleContainersDimmingConsecutively() {
- TestWindowContainer first = mChild;
- TestWindowContainer second = new TestWindowContainer(mWm);
- mHost.addChild(first, 0);
- mHost.addChild(second, 1);
-
- mDimmer.adjustAppearance(first, 0.5f, 0);
- mDimmer.adjustPosition(mChild, first, -1);
+ mDimmer.adjustAppearance(mChild1, 0.5f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
mDimmer.resetDimStates();
- mDimmer.adjustAppearance(second, 0.9f, 0);
- mDimmer.adjustPosition(mChild, second, -1);
+ mDimmer.adjustAppearance(mChild2, 0.9f, 0);
+ mDimmer.adjustPosition(mChild1, mChild2);
mDimmer.updateDims(mTransaction);
verify(sTestAnimation, times(2)).startAnimation(
@@ -353,16 +325,11 @@
*/
@Test
public void testMultipleContainersDimmingAtTheSameTime() {
- TestWindowContainer first = mChild;
- TestWindowContainer second = new TestWindowContainer(mWm);
- mHost.addChild(first, 0);
- mHost.addChild(second, 1);
-
- mDimmer.adjustAppearance(first, 0.5f, 0);
- mDimmer.adjustPosition(mChild, first, -1);
+ mDimmer.adjustAppearance(mChild1, 0.5f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
- mDimmer.adjustAppearance(second, 0.9f, 0);
- mDimmer.adjustPosition(mChild, second, -1);
+ mDimmer.adjustAppearance(mChild2, 0.9f, 0);
+ mDimmer.adjustPosition(mChild1, mChild2);
mDimmer.updateDims(mTransaction);
verify(sTestAnimation, times(1)).startAnimation(
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
index affaad6..14276ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -277,7 +277,7 @@
mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
mWmInternal.waitForAllWindowsDrawn(mScreenUnblocker,
- /* timeout= */ Integer.MAX_VALUE, DEFAULT_DISPLAY);
+ /* timeout= */ Integer.MAX_VALUE, INVALID_DISPLAY);
mWmInternal.waitForAllWindowsDrawn(mSecondaryScreenUnblocker,
/* timeout= */ Integer.MAX_VALUE, mSecondaryDisplayContent.getDisplayId());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index f2ea1c9..eca4d21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1144,6 +1144,7 @@
@Test
public void testOrientationBehind() {
+ assertNull(mDisplayContent.getLastOrientationSource());
final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true)
.setScreenOrientation(getRotatedOrientation(mDisplayContent)).build();
prev.setVisibleRequested(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index caeb41c..f32a234 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -284,11 +284,11 @@
final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
policy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs);
- policy.screenTurnedOff();
+ policy.screenTurnedOff(false /* acquireSleepToken */);
policy.setAwake(false);
policy.screenTurningOn(null /* screenOnListener */);
assertTrue(wpc.isShowingUiWhileDozing());
- policy.screenTurnedOff();
+ policy.screenTurnedOff(false /* acquireSleepToken */);
assertFalse(wpc.isShowingUiWhileDozing());
policy.screenTurningOn(null /* screenOnListener */);
@@ -393,7 +393,7 @@
info.logicalWidth, info.logicalHeight).mConfigFrame);
// If screen is not fully turned on, then the cache should be preserved.
- displayPolicy.screenTurnedOff();
+ displayPolicy.screenTurnedOff(false /* acquireSleepToken */);
final TransitionController transitionController = mDisplayContent.mTransitionController;
spyOn(transitionController);
doReturn(true).when(transitionController).isCollecting();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 2e488d8..09fe75d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -22,6 +22,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
@@ -78,7 +79,6 @@
import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -86,7 +86,6 @@
import com.android.server.testutils.TestHandler;
import com.android.server.wm.DisplayContent.FixedRotationTransitionListener;
-import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -168,25 +167,10 @@
public void setUp() {
FakeSettingsProvider.clearSettingsProvider();
- mPreviousStatusBarManagerInternal = LocalServices.getService(
- StatusBarManagerInternal.class);
- LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
- mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
- LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
mDisplayRotationImmersiveAppCompatPolicyMock = null;
mBuilder = new DisplayRotationBuilder();
}
- @After
- public void tearDown() {
- LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
- if (mPreviousStatusBarManagerInternal != null) {
- LocalServices.addService(StatusBarManagerInternal.class,
- mPreviousStatusBarManagerInternal);
- mPreviousStatusBarManagerInternal = null;
- }
- }
-
// ================================
// Display Settings Related Tests
// ================================
@@ -677,7 +661,8 @@
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
assertTrue(waitForUiHandler());
- verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
+ verify(mMockStatusBarManagerInternal).onProposedRotationChanged(DEFAULT_DISPLAY,
+ Surface.ROTATION_90, true);
}
@Test
@@ -697,7 +682,8 @@
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
assertTrue(waitForUiHandler());
- verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
+ verify(mMockStatusBarManagerInternal).onProposedRotationChanged(DEFAULT_DISPLAY,
+ Surface.ROTATION_90, true);
// An imaginary ActivityRecord.setRequestedOrientation call disables immersive mode:
when(mDisplayRotationImmersiveAppCompatPolicyMock.isRotationLockEnforced(
@@ -728,7 +714,7 @@
assertTrue(waitForUiHandler());
verify(mMockStatusBarManagerInternal)
- .onProposedRotationChanged(Surface.ROTATION_180, true);
+ .onProposedRotationChanged(DEFAULT_DISPLAY, Surface.ROTATION_180, true);
}
@Test
@@ -746,7 +732,7 @@
assertTrue(waitForUiHandler());
verify(mMockStatusBarManagerInternal)
- .onProposedRotationChanged(Surface.ROTATION_180, false);
+ .onProposedRotationChanged(DEFAULT_DISPLAY, Surface.ROTATION_180, false);
}
@Test
@@ -773,7 +759,7 @@
assertTrue(waitForUiHandler());
verify(mMockStatusBarManagerInternal)
- .onProposedRotationChanged(Surface.ROTATION_180, false);
+ .onProposedRotationChanged(DEFAULT_DISPLAY, Surface.ROTATION_180, false);
}
@Test
@@ -1526,6 +1512,7 @@
mMockDisplayContent = mock(DisplayContent.class);
when(mMockDisplayContent.getDisplay()).thenReturn(mock(Display.class));
+ when(mMockDisplayContent.getDisplayId()).thenReturn(DEFAULT_DISPLAY);
mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt()))
.thenReturn(NO_CUTOUT);
@@ -1541,6 +1528,9 @@
field.set(mMockDisplayContent, mock(FixedRotationTransitionListener.class));
mMockDisplayPolicy = mock(DisplayPolicy.class);
+ mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
+ when(mMockDisplayPolicy.getStatusBarManagerInternal()).thenReturn(
+ mMockStatusBarManagerInternal);
mMockRes = mock(Resources.class);
when(mMockContext.getResources()).thenReturn((mMockRes));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 57839e2..ffe3893 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -244,6 +244,19 @@
mWm.clearForcedDisplaySize(mSecondaryDisplay.getDisplayId());
assertEquals(mSecondaryDisplay.mInitialDisplayWidth, mSecondaryDisplay.mBaseDisplayWidth);
assertEquals(mSecondaryDisplay.mInitialDisplayHeight, mSecondaryDisplay.mBaseDisplayHeight);
+
+ // Forced size can be cleared even if the initial display size is smaller than max width.
+ final int maxWidth = mSecondaryDisplay.mInitialDisplayWidth - 20;
+ mSecondaryDisplay.setMaxUiWidth(maxWidth);
+ assertEquals(maxWidth, mSecondaryDisplay.mBaseDisplayWidth);
+ mSecondaryDisplay.setForcedSize(maxWidth - 10, maxWidth + 10);
+ assertNotEquals(maxWidth, mSecondaryDisplay.mBaseDisplayHeight);
+ assertTrue(mSecondaryDisplay.mIsSizeForced);
+
+ mWm.clearForcedDisplaySize(mSecondaryDisplay.mDisplayId);
+ assertFalse(mSecondaryDisplay.mIsSizeForced);
+ assertEquals(maxWidth, mSecondaryDisplay.mBaseDisplayWidth);
+ assertEquals(0, mSettingsProvider.getSettings(originalInfo).mForcedWidth);
}
@Test
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/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 1d14dc3..bef4531 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -219,7 +219,7 @@
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// THEN a pinning request should be shown
- verify(mStatusBarManagerInternal).showScreenPinningRequest(anyInt());
+ verify(mStatusBarManagerInternal).showScreenPinningRequest(anyInt(), anyInt());
}
@Test
@@ -769,9 +769,11 @@
private Task getTask(Intent intent, int lockTaskAuth) {
Task tr = mock(Task.class);
+ DisplayContent dc = mock(DisplayContent.class);
tr.mLockTaskAuth = lockTaskAuth;
tr.intent = intent;
tr.mUserId = TEST_USER_ID;
+ tr.mDisplayContent = dc;
return tr;
}
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/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 9981a4d..9967cce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -36,7 +36,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceSession;
import androidx.test.filters.SmallTest;
@@ -69,7 +68,6 @@
@Mock AnimationAdapter mSpec2;
@Mock Transaction mTransaction;
- private SurfaceSession mSession = new SurfaceSession();
private MyAnimatable mAnimatable;
private MyAnimatable mAnimatable2;
private DeferFinishAnimatable mDeferFinishAnimatable;
@@ -78,9 +76,9 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mAnimatable = new MyAnimatable(mWm, mSession, mTransaction);
- mAnimatable2 = new MyAnimatable(mWm, mSession, mTransaction);
- mDeferFinishAnimatable = new DeferFinishAnimatable(mWm, mSession, mTransaction);
+ mAnimatable = new MyAnimatable(mWm, mTransaction);
+ mAnimatable2 = new MyAnimatable(mWm, mTransaction);
+ mDeferFinishAnimatable = new DeferFinishAnimatable(mWm, mTransaction);
}
@After
@@ -88,8 +86,6 @@
mAnimatable = null;
mAnimatable2 = null;
mDeferFinishAnimatable = null;
- mSession.kill();
- mSession = null;
}
@Test
@@ -313,7 +309,6 @@
private static class MyAnimatable implements Animatable {
- private final SurfaceSession mSession;
private final Transaction mTransaction;
final SurfaceControl mParent;
final SurfaceControl mSurface;
@@ -322,13 +317,12 @@
boolean mFinishedCallbackCalled;
@AnimationType int mFinishedAnimationType;
- MyAnimatable(WindowManagerService wm, SurfaceSession session, Transaction transaction) {
- mSession = session;
+ MyAnimatable(WindowManagerService wm, Transaction transaction) {
mTransaction = transaction;
- mParent = wm.makeSurfaceBuilder(mSession)
+ mParent = wm.makeSurfaceBuilder()
.setName("test surface parent")
.build();
- mSurface = wm.makeSurfaceBuilder(mSession)
+ mSurface = wm.makeSurfaceBuilder()
.setName("test surface")
.build();
mFinishedCallbackCalled = false;
@@ -360,7 +354,7 @@
@Override
public Builder makeAnimationLeash() {
- return new Builder(mSession) {
+ return new Builder() {
@Override
public SurfaceControl build() {
@@ -406,9 +400,8 @@
Runnable mEndDeferFinishCallback;
- DeferFinishAnimatable(WindowManagerService wm, SurfaceSession session,
- Transaction transaction) {
- super(wm, session, transaction);
+ DeferFinishAnimatable(WindowManagerService wm, Transaction transaction) {
+ super(wm, transaction);
}
@Override
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 1e39f0b..71cfbfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -394,7 +394,7 @@
mWmService = WindowManagerService.main(
mContext, mImService, false, wmPolicy, mAtmService,
testDisplayWindowSettingsProvider, StubTransaction::new,
- (unused) -> new MockSurfaceControlBuilder());
+ MockSurfaceControlBuilder::new);
spyOn(mWmService);
spyOn(mWmService.mRoot);
// Invoked during {@link ActivityStack} creation.
@@ -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/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index d013053..0cd036f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -90,6 +90,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.ITaskFragmentOrganizer;
@@ -139,6 +140,7 @@
private IBinder mFragmentToken;
private WindowContainerTransaction mTransaction;
private WindowContainerToken mFragmentWindowToken;
+ private RemoteAnimationDefinition mDefinition;
private IBinder mErrorToken;
private Rect mTaskFragBounds;
@@ -167,6 +169,7 @@
mTransaction = new WindowContainerTransaction();
mTransaction.setTaskFragmentOrganizer(mIOrganizer);
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
+ mDefinition = new RemoteAnimationDefinition();
mErrorToken = new Binder();
final Rect displayBounds = mDisplayContent.getBounds();
mTaskFragBounds = new Rect(displayBounds.left, displayBounds.top, displayBounds.centerX(),
@@ -576,6 +579,17 @@
}
@Test
+ public void testRegisterRemoteAnimations() {
+ mController.registerRemoteAnimations(mIOrganizer, mDefinition);
+
+ assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer));
+
+ mController.unregisterRemoteAnimations(mIOrganizer);
+
+ assertNull(mController.getRemoteAnimationDefinition(mIOrganizer));
+ }
+
+ @Test
public void testApplyTransaction_disallowRemoteTransitionForNonSystemOrganizer() {
mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
@@ -2121,8 +2135,8 @@
private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
doReturn(mockParent).when(taskFragment).getTask();
doReturn(new TaskFragmentParentInfo(
- new Configuration(), DEFAULT_DISPLAY, true, true, null /* decorSurface */))
- .when(mockParent).getTaskFragmentParentInfo();
+ new Configuration(), DEFAULT_DISPLAY, mockParent.mTaskId, true, true,
+ null /* decorSurface */)).when(mockParent).getTaskFragmentParentInfo();
// Task needs to be visible
mockParent.lastActiveTime = 100;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index cc1805a..fd959b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -242,7 +242,7 @@
final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds());
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
- displayPolicy.screenTurnedOff();
+ displayPolicy.screenTurnedOff(false /* acquireSleepToken */);
assertFalse(mTaskFragment.okToAnimate());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index d62c626..eebb487 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -60,7 +60,7 @@
@Override
public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
- int[] outAppOp) {
+ int[] outAppOp, int displayId) {
return 0;
}
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..adf878c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -51,6 +51,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static org.junit.Assert.assertEquals;
@@ -140,8 +141,7 @@
}
private Transition createTestTransition(int transitType) {
- final TransitionController controller = new TestTransitionController(
- mock(ActivityTaskManagerService.class));
+ final TransitionController controller = new TestTransitionController(mAtm);
mSyncEngine = createTestBLASTSyncEngine();
controller.setSyncEngine(mSyncEngine);
@@ -1590,10 +1590,10 @@
});
assertTrue(activity1.isVisible());
doReturn(false).when(task1).isTranslucent(null);
- doReturn(false).when(task1).isTranslucentForTransition();
+ doReturn(false).when(task1).isTranslucentAndVisible();
assertTrue(controller.canApplyDim(task1));
doReturn(true).when(task1).isTranslucent(null);
- doReturn(true).when(task1).isTranslucentForTransition();
+ doReturn(true).when(task1).isTranslucentAndVisible();
assertFalse(controller.canApplyDim(task1));
controller.finishTransition(ActionChain.testFinish(closeTransition));
@@ -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
@@ -2358,7 +2357,7 @@
}
@Test
- public void testMoveToTopWhileVisible() {
+ public void testMoveTaskToTopWhileVisible() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
final ArraySet<WindowContainer> participants = transition.mParticipants;
@@ -2393,6 +2392,55 @@
assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
+ public void testMoveDisplayToTop() {
+ // Set up two displays, each of which has a task.
+ DisplayContent otherDisplay = createNewDisplay();
+ final Consumer<DisplayContent> setUpTask = (DisplayContent dc) -> {
+ final Task task = createTask(dc);
+ final ActivityRecord act = createActivityRecord(task);
+ final TestWindowState win = createWindowState(
+ new WindowManager.LayoutParams(TYPE_BASE_APPLICATION), act);
+ act.addWindow(win);
+ act.setVisibleRequested(true);
+ };
+ setUpTask.accept(mDisplayContent);
+ setUpTask.accept(otherDisplay);
+ mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /* updateImWindows */);
+
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ // Emulate WindowManagerService#moveDisplayToTopInternal().
+ transition.recordTaskOrder(mDefaultDisplay);
+ mDefaultDisplay.getParent().positionChildAt(WindowContainer.POSITION_TOP,
+ mDefaultDisplay, true /* includingParents */);
+ mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /* updateImWindows */);
+ transition.setReady(mDefaultDisplay, true /* ready */);
+
+ // Test has order changes, a shallow check of order changes.
+ assertTrue(transition.hasOrderChanges());
+
+ // We just moved a display to top, so there shouldn't be any changes.
+ ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
+ participants, changes);
+ assertTrue(targets.isEmpty());
+
+ // After collecting order changes, the task on the newly focused display should be
+ // considered to get moved to top.
+ transition.collectOrderChanges(true);
+ targets = Transition.calculateTargets(participants, changes);
+ assertEquals(1, targets.size());
+
+ // Make sure the flag is set
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, mMockT);
+ assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0);
+ assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
+ }
+
private class OrderChangeTestSetup {
final TransitionController mController;
final TestTransitionPlayer mPlayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 39640fb..6111a65 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -554,6 +554,14 @@
verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
any(), any(), anyInt(), any(), anyBoolean());
+ // Even if the given display id is INVALID_DISPLAY, the specified params.token should be
+ // able to map the corresponding display.
+ final int result = mWm.addWindow(
+ session, new TestIWindow(), params, View.VISIBLE, INVALID_DISPLAY,
+ UserHandle.USER_SYSTEM, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
+ new InsetsSourceControl.Array(), new Rect(), new float[1]);
+ assertThat(result).isAtLeast(WindowManagerGlobal.ADD_OKAY);
+
assertTrue(parentWin.hasChild());
assertTrue(parentWin.isAttached());
session.binderDied();
@@ -1189,7 +1197,7 @@
final int displayId = mDisplayContent.mDisplayId;
// Use real surface, so ViewportWindow's BlastBufferQueue can be created.
final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>();
- mWm.mSurfaceControlFactory = s -> new SurfaceControl.Builder() {
+ mWm.mSurfaceControlFactory = () -> new SurfaceControl.Builder() {
@Override
public SurfaceControl build() {
final SurfaceControl sc = super.build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 88ce3a6..4f60106 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -51,7 +51,6 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.window.ScreenCapture;
import androidx.test.filters.SmallTest;
@@ -63,7 +62,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
-import java.util.function.Function;
+import java.util.function.Supplier;
/**
* Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method.
@@ -126,8 +125,7 @@
private LayerRecordingTransaction mTransaction;
private SurfaceControl mPendingParent;
- HierarchyRecorder(SurfaceSession s, LayerRecordingTransaction transaction) {
- super(s);
+ HierarchyRecorder(LayerRecordingTransaction transaction) {
mTransaction = transaction;
}
@@ -146,8 +144,8 @@
}
}
- private static class HierarchyRecordingBuilderFactory implements Function<SurfaceSession,
- SurfaceControl.Builder> {
+ private static class HierarchyRecordingBuilderFactory
+ implements Supplier<SurfaceControl.Builder> {
private LayerRecordingTransaction mTransaction;
HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) {
@@ -155,9 +153,8 @@
}
@Override
- public SurfaceControl.Builder apply(SurfaceSession s) {
- final LayerRecordingTransaction transaction = mTransaction;
- return new HierarchyRecorder(s, transaction);
+ public SurfaceControl.Builder get() {
+ return new HierarchyRecorder(mTransaction);
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index ff4be55..d89c9c1 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2339,6 +2339,7 @@
* @return True if the digits were processed as an MMI code, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean handleMmi(String dialString) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -2364,6 +2365,7 @@
* @return True if the digits were processed as an MMI code, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
ITelecomService service = getTelecomService();
if (service != null) {
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 112471b..c85374e 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -376,7 +376,8 @@
*/
void requestLogMark(in String message);
- void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
+ void setTestPhoneAcctSuggestionComponent(String flattenedComponentName,
+ in UserHandle userHandle);
void setTestDefaultCallScreeningApp(String packageName);
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index feea55b..a678147 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -33,7 +33,6 @@
import android.widget.Toast;
import com.android.internal.telephony.TelephonyPermissions;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
/**
@@ -312,18 +311,10 @@
// This avoid breaking legacy code that rely on public-facing APIs to access cell location,
// and it doesn't create an info leak risk because the cell location is stored in the phone
// process anyway, and the system server already has location access.
- if (Flags.supportPhoneUidCheckForMultiuser()) {
- if (TelephonyPermissions.isSystemOrPhone(query.callingUid)
- || UserHandle.isSameApp(query.callingUid, Process.NETWORK_STACK_UID)
- || UserHandle.isSameApp(query.callingUid, Process.ROOT_UID)) {
- return LocationPermissionResult.ALLOWED;
- }
- } else {
- if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
- || query.callingUid == Process.NETWORK_STACK_UID
- || query.callingUid == Process.ROOT_UID) {
- return LocationPermissionResult.ALLOWED;
- }
+ if (TelephonyPermissions.isSystemOrPhone(query.callingUid)
+ || UserHandle.isSameApp(query.callingUid, Process.NETWORK_STACK_UID)
+ || UserHandle.isSameApp(query.callingUid, Process.ROOT_UID)) {
+ return LocationPermissionResult.ALLOWED;
}
// Check the system-wide requirements. If the location main switch is off and the caller is
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/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 7ed26fb..b80610a 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -35,7 +35,6 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.Flags;
import java.util.HashMap;
import java.util.HashSet;
@@ -934,12 +933,8 @@
* as system user or not.
*/
public static boolean isSystemOrPhone(int uid) {
- if (Flags.supportPhoneUidCheckForMultiuser()) {
- return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || UserHandle.isSameApp(uid,
- Process.PHONE_UID);
- } else {
- return uid == Process.SYSTEM_UID || uid == Process.PHONE_UID;
- }
+ return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || UserHandle.isSameApp(uid,
+ Process.PHONE_UID);
}
/**
@@ -947,11 +942,7 @@
* as system user or not.
*/
public static boolean isRootOrShell(int uid) {
- if (Flags.supportPhoneUidCheckForMultiuser()) {
- return UserHandle.isSameApp(uid, Process.ROOT_UID) || UserHandle.isSameApp(uid,
- Process.SHELL_UID);
- } else {
- return uid == Process.ROOT_UID || uid == Process.SHELL_UID;
- }
+ return UserHandle.isSameApp(uid, Process.ROOT_UID) || UserHandle.isSameApp(uid,
+ Process.SHELL_UID);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cba2eea..41223db 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5030,6 +5030,18 @@
public static final String KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY =
KEY_PREFIX + "es_supl_data_plane_only_roaming_plmn_string_array";
+ /**
+ * Determine whether to enable Net Initiated SUPL (NI SUPL) message injection.
+ * If enabled, the GnssLocationProvider will monitor for WAP PUSH or MT SMS NI SUPL intents
+ * and subsequently inject the NI SUPL packet into the GNSS HAL.
+ * {@code false} - Disable NI SUPL message injection. This is default.
+ * {@code true} - Enable NI SUPL message injection.
+ */
+ @FlaggedApi(android.location.flags.Flags
+ .FLAG_ENABLE_NI_SUPL_MESSAGE_INJECTION_BY_CARRIER_CONFIG)
+ public static final String KEY_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL =
+ KEY_PREFIX + "enable_ni_supl_message_injection_bool";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -5047,6 +5059,9 @@
defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
defaults.putStringArray(KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY, null);
+ if (android.location.flags.Flags.enableNiSuplMessageInjectionByCarrierConfig()) {
+ defaults.putBoolean(KEY_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL, false);
+ }
return defaults;
}
}
@@ -10014,6 +10029,19 @@
public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING =
"satellite_nidd_apn_name_string";
+ /**
+ * Default value {@code true}, meaning when an emergency call request comes in, if the device is
+ * in emergency satellite mode but hasn't sent the first satellite datagram, then exits
+ * satellite mode to allow the emergency call to go through.
+ *
+ * If {@code false}, the emergency call is always blocked if device is in emergency satellite
+ * mode. Note if device is NOT in emergency satellite mode, emergency call is always allowed.
+ *
+ * @hide
+ */
+ public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL =
+ "satellite_roaming_turn_off_session_for_emergency_call_bool";
+
/** @hide */
@IntDef({
CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC,
@@ -10085,8 +10113,8 @@
* The default value is 30 seconds.
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT =
- "satellite_screen_off_inactivity_timeout_sec_int";
+ public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT =
+ "satellite_roaming_screen_off_inactivity_timeout_sec_int";
/**
* An integer key holds the timeout duration in seconds used to determine whether to exit P2P
@@ -10099,8 +10127,8 @@
* The default value is 180 seconds.
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static final String KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT =
- "satellite_p2p_sms_inactivity_timeout_sec_int";
+ public static final String KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT =
+ "satellite_roaming_p2p_sms_inactivity_timeout_sec_int";
/**
* An integer key holds the timeout duration in seconds used to determine whether to exit ESOS
@@ -10113,8 +10141,8 @@
* The default value is 600 seconds.
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static final String KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT =
- "satellite_esos_inactivity_timeout_sec_int";
+ public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT =
+ "satellite_roaming_esos_inactivity_timeout_sec_int";
/**
* Indicating whether DUN APN should be disabled when the device is roaming. In that case,
@@ -11276,13 +11304,14 @@
sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, false);
sDefaults.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL, true);
sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0);
sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911);
sDefaults.putInt(KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 180);
- sDefaults.putInt(KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30);
- sDefaults.putInt(KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, 180);
- sDefaults.putInt(KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT, 600);
+ sDefaults.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30);
+ sDefaults.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, 180);
+ sDefaults.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, 600);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f0850af..51e0c33 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1456,21 +1456,21 @@
public static final int SERVICE_CAPABILITY_MAX = SERVICE_CAPABILITY_DATA;
/**
- * Bitmask for {@code SERVICE_CAPABILITY_VOICE}.
+ * Bitmask for {@link #SERVICE_CAPABILITY_VOICE}.
* @hide
*/
public static final int SERVICE_CAPABILITY_VOICE_BITMASK =
serviceCapabilityToBitmask(SERVICE_CAPABILITY_VOICE);
/**
- * Bitmask for {@code SERVICE_CAPABILITY_SMS}.
+ * Bitmask for {@link #SERVICE_CAPABILITY_SMS}.
* @hide
*/
public static final int SERVICE_CAPABILITY_SMS_BITMASK =
serviceCapabilityToBitmask(SERVICE_CAPABILITY_SMS);
/**
- * Bitmask for {@code SERVICE_CAPABILITY_DATA}.
+ * Bitmask for {@link #SERVICE_CAPABILITY_DATA}.
* @hide
*/
public static final int SERVICE_CAPABILITY_DATA_BITMASK =
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7481daa..3e226cc 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -23,6 +23,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
import android.Manifest;
+import android.annotation.BoolRes;
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
import android.annotation.CurrentTimeMillisLong;
@@ -132,6 +133,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import java.io.IOException;
@@ -6885,7 +6887,26 @@
}
}
- // TODO(b/316183370): replace all @code with @link in javadoc after feature is released
+ // Suppressing AndroidFrameworkCompatChange because we're querying vendor
+ // partition SDK level, not application's target SDK version.
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ private boolean hasCapability(@NonNull String feature, @BoolRes int legacySetting) {
+ if (mContext == null) return true;
+
+ if (mContext.getPackageManager().hasSystemFeature(feature)) return true;
+
+ // Check SDK version of the vendor partition.
+ final int vendorApiLevel = SystemProperties.getInt(
+ "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
+ // Devices shipped with 2024Q2 or later are required to declare FEATURE_TELEPHONY_*
+ // for individual sub-features (calling, messaging, data), so there's no need to check
+ // the legacy setting.
+ if (vendorApiLevel < Build.VENDOR_API_2024_Q2) {
+ return mContext.getResources().getBoolean(legacySetting);
+ }
+ return false;
+ }
+
/**
* @return true if the current device is "voice capable".
* <p>
@@ -6899,16 +6920,14 @@
* PackageManager.FEATURE_TELEPHONY system feature, which is available
* on any device with a telephony radio, even if the device is
* data-only.
- * @deprecated Replaced by {@code #isDeviceVoiceCapable()}. Starting from Android 15, voice
+ * @deprecated Replaced by {@link #isDeviceVoiceCapable()}. Starting from Android 15, voice
* capability may also be overridden by carriers for a given subscription. For voice capable
- * device (when {@code #isDeviceVoiceCapable} return {@code true}), caller should check for
- * subscription-level voice capability as well. See {@code #isDeviceVoiceCapable} for details.
+ * device (when {@link #isDeviceVoiceCapable} return {@code true}), caller should check for
+ * subscription-level voice capability as well. See {@link #isDeviceVoiceCapable} for details.
*/
- @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@Deprecated
public boolean isVoiceCapable() {
- if (mContext == null) return true;
- return mContext.getResources().getBoolean(
+ return hasCapability(PackageManager.FEATURE_TELEPHONY_CALLING,
com.android.internal.R.bool.config_voice_capable);
}
@@ -6926,12 +6945,11 @@
* <p>
* Starting from Android 15, voice capability may also be overridden by carrier for a given
* subscription on a voice capable device. To check if a subscription is "voice capable",
- * call method {@code SubscriptionInfo#getServiceCapabilities()} and check if
- * {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE} is included.
+ * call method {@link SubscriptionInfo#getServiceCapabilities()} and check if
+ * {@link SubscriptionManager#SERVICE_CAPABILITY_VOICE} is included.
*
* @see SubscriptionInfo#getServiceCapabilities()
*/
- @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public boolean isDeviceVoiceCapable() {
return isVoiceCapable();
@@ -6945,15 +6963,14 @@
* <p>
* Note: Voicemail waiting sms, cell broadcasting sms, and MMS are
* disabled when device doesn't support sms.
- * @deprecated Replaced by {@code #isDeviceSmsCapable()}. Starting from Android 15, SMS
+ * @deprecated Replaced by {@link #isDeviceSmsCapable()}. Starting from Android 15, SMS
* capability may also be overridden by carriers for a given subscription. For SMS capable
- * device (when {@code #isDeviceSmsCapable} return {@code true}), caller should check for
- * subscription-level SMS capability as well. See {@code #isDeviceSmsCapable} for details.
+ * device (when {@link #isDeviceSmsCapable} return {@code true}), caller should check for
+ * subscription-level SMS capability as well. See {@link #isDeviceSmsCapable} for details.
*/
- @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+ @Deprecated
public boolean isSmsCapable() {
- if (mContext == null) return true;
- return mContext.getResources().getBoolean(
+ return hasCapability(PackageManager.FEATURE_TELEPHONY_MESSAGING,
com.android.internal.R.bool.config_sms_capable);
}
@@ -6968,12 +6985,11 @@
* <p>
* Starting from Android 15, SMS capability may also be overridden by carriers for a given
* subscription on an SMS capable device. To check if a subscription is "SMS capable",
- * call method {@code SubscriptionInfo#getServiceCapabilities()} and check if
- * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS} is included.
+ * call method {@link SubscriptionInfo#getServiceCapabilities()} and check if
+ * {@link SubscriptionManager#SERVICE_CAPABILITY_SMS} is included.
*
* @see SubscriptionInfo#getServiceCapabilities()
*/
- @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public boolean isDeviceSmsCapable() {
return isSmsCapable();
@@ -9975,6 +9991,7 @@
ALLOWED_NETWORK_TYPES_REASON_POWER,
ALLOWED_NETWORK_TYPES_REASON_CARRIER,
ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G,
+ ALLOWED_NETWORK_TYPES_REASON_TEST,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AllowedNetworkTypesReason {
@@ -10013,6 +10030,14 @@
public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
/**
+ * To indicate allowed network type change is requested by Testing purpose, should be default to
+ * {@link #getAllNetworkTypesBitmask} when done testing.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_NETWORK_TYPES_REASON_TEST = 4;
+
+ /**
* Set the allowed network types of the device and provide the reason triggering the allowed
* network change.
* <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
@@ -10118,6 +10143,8 @@
case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
return true;
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_TEST:
+ return TelephonyUtils.IS_DEBUGGABLE;
}
return false;
}
@@ -14530,10 +14557,8 @@
* data connections over the telephony network.
* <p>
*/
- @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataCapable() {
- if (mContext == null) return true;
- return mContext.getResources().getBoolean(
+ return hasCapability(PackageManager.FEATURE_TELEPHONY_DATA,
com.android.internal.R.bool.config_mobile_data_capable);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index c5934a7..284e2bd 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -412,6 +412,22 @@
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS = 27;
+ /**
+ * Disabling satellite is in progress.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int SATELLITE_RESULT_DISABLE_IN_PROGRESS = 28;
+
+ /**
+ * Enabling satellite is in progress.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int SATELLITE_RESULT_ENABLE_IN_PROGRESS = 29;
+
/** @hide */
@IntDef(prefix = {"SATELLITE_RESULT_"}, value = {
SATELLITE_RESULT_SUCCESS,
@@ -441,7 +457,9 @@
SATELLITE_RESULT_MODEM_TIMEOUT,
SATELLITE_RESULT_LOCATION_DISABLED,
SATELLITE_RESULT_LOCATION_NOT_AVAILABLE,
- SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS
+ SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS,
+ SATELLITE_RESULT_DISABLE_IN_PROGRESS,
+ SATELLITE_RESULT_ENABLE_IN_PROGRESS
})
@Retention(RetentionPolicy.SOURCE)
public @interface SatelliteResult {}
@@ -592,7 +610,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.requestSatelliteEnabled(mSubId, attributes.isEnabled(),
+ telephony.requestSatelliteEnabled(attributes.isEnabled(),
attributes.isDemoMode(), attributes.isEmergencyMode(), errorCallback);
} else {
Rlog.e(TAG, "requestEnabled() invalid telephony");
@@ -650,7 +668,7 @@
}
}
};
- telephony.requestIsSatelliteEnabled(mSubId, receiver);
+ telephony.requestIsSatelliteEnabled(receiver);
} else {
loge("requestIsEnabled() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -707,7 +725,7 @@
}
}
};
- telephony.requestIsDemoModeEnabled(mSubId, receiver);
+ telephony.requestIsDemoModeEnabled(receiver);
} else {
loge("requestIsDemoModeEnabled() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -764,7 +782,7 @@
}
}
};
- telephony.requestIsEmergencyModeEnabled(mSubId, receiver);
+ telephony.requestIsEmergencyModeEnabled(receiver);
} else {
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
@@ -821,7 +839,7 @@
}
}
};
- telephony.requestIsSatelliteSupported(mSubId, receiver);
+ telephony.requestIsSatelliteSupported(receiver);
} else {
loge("requestIsSupported() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -878,7 +896,7 @@
}
}
};
- telephony.requestSatelliteCapabilities(mSubId, receiver);
+ telephony.requestSatelliteCapabilities(receiver);
} else {
loge("requestCapabilities() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -1204,8 +1222,7 @@
}
};
sSatelliteTransmissionUpdateCallbackMap.put(callback, internalCallback);
- telephony.startSatelliteTransmissionUpdates(mSubId, errorCallback,
- internalCallback);
+ telephony.startSatelliteTransmissionUpdates(errorCallback, internalCallback);
} else {
loge("startTransmissionUpdates() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
@@ -1255,8 +1272,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.stopSatelliteTransmissionUpdates(mSubId, errorCallback,
- internalCallback);
+ telephony.stopSatelliteTransmissionUpdates(errorCallback, internalCallback);
// TODO: Notify SmsHandler that pointing UI stopped
} else {
loge("stopSatelliteTransmissionUpdates: No internal callback.");
@@ -1312,7 +1328,7 @@
() -> resultListener.accept(result)));
}
};
- cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData,
+ cancelRemote = telephony.provisionSatelliteService(token, provisionData,
errorCallback);
} else {
loge("provisionService() invalid telephony");
@@ -1364,7 +1380,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.deprovisionSatelliteService(mSubId, token, errorCallback);
+ telephony.deprovisionSatelliteService(token, errorCallback);
} else {
loge("deprovisionService() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
@@ -1419,8 +1435,7 @@
}
};
sSatelliteProvisionStateCallbackMap.put(callback, internalCallback);
- return telephony.registerForSatelliteProvisionStateChanged(
- mSubId, internalCallback);
+ return telephony.registerForSatelliteProvisionStateChanged(internalCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1453,7 +1468,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
if (internalCallback != null) {
- telephony.unregisterForSatelliteProvisionStateChanged(mSubId, internalCallback);
+ telephony.unregisterForSatelliteProvisionStateChanged(internalCallback);
} else {
loge("unregisterForProvisionStateChanged: No internal callback.");
}
@@ -1510,7 +1525,7 @@
}
}
};
- telephony.requestIsSatelliteProvisioned(mSubId, receiver);
+ telephony.requestIsSatelliteProvisioned(receiver);
} else {
loge("requestIsProvisioned() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -1560,7 +1575,7 @@
}
};
sSatelliteModemStateCallbackMap.put(callback, internalCallback);
- return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback);
+ return telephony.registerForSatelliteModemStateChanged(internalCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1593,7 +1608,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
if (internalCallback != null) {
- telephony.unregisterForModemStateChanged(mSubId, internalCallback);
+ telephony.unregisterForModemStateChanged(internalCallback);
} else {
loge("unregisterForModemStateChanged: No internal callback.");
}
@@ -1656,7 +1671,7 @@
}
};
sSatelliteDatagramCallbackMap.put(callback, internalCallback);
- return telephony.registerForIncomingDatagram(mSubId, internalCallback);
+ return telephony.registerForIncomingDatagram(internalCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1688,7 +1703,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
if (internalCallback != null) {
- telephony.unregisterForIncomingDatagram(mSubId, internalCallback);
+ telephony.unregisterForIncomingDatagram(internalCallback);
} else {
loge("unregisterForIncomingDatagram: No internal callback.");
}
@@ -1732,7 +1747,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.pollPendingDatagrams(mSubId, internalCallback);
+ telephony.pollPendingDatagrams(internalCallback);
} else {
loge("pollPendingDatagrams() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
@@ -1790,7 +1805,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.sendDatagram(mSubId, datagramType, datagram,
+ telephony.sendDatagram(datagramType, datagram,
needFullScreenPointingUI, internalCallback);
} else {
loge("sendDatagram() invalid telephony");
@@ -1908,7 +1923,7 @@
}
}
};
- telephony.requestTimeForNextSatelliteVisibility(mSubId, receiver);
+ telephony.requestTimeForNextSatelliteVisibility(receiver);
} else {
loge("requestTimeForNextSatelliteVisibility() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -1939,7 +1954,7 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- telephony.setDeviceAlignedWithSatellite(mSubId, isAligned);
+ telephony.setDeviceAlignedWithSatellite(isAligned);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -2203,7 +2218,7 @@
}
}
};
- telephony.requestNtnSignalStrength(mSubId, receiver);
+ telephony.requestNtnSignalStrength(receiver);
} else {
loge("requestNtnSignalStrength() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -2254,7 +2269,7 @@
ntnSignalStrength)));
}
};
- telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback);
+ telephony.registerForNtnSignalStrengthChanged(internalCallback);
sNtnSignalStrengthCallbackMap.put(callback, internalCallback);
} else {
throw new IllegalStateException("Telephony service is null.");
@@ -2294,7 +2309,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
if (internalCallback != null) {
- telephony.unregisterForNtnSignalStrengthChanged(mSubId, internalCallback);
+ telephony.unregisterForNtnSignalStrengthChanged(internalCallback);
} else {
loge("unregisterForNtnSignalStrengthChanged: No internal callback.");
throw new IllegalArgumentException("callback is not valid");
@@ -2339,7 +2354,7 @@
}
};
sSatelliteCapabilitiesCallbackMap.put(callback, internalCallback);
- return telephony.registerForCapabilitiesChanged(mSubId, internalCallback);
+ return telephony.registerForCapabilitiesChanged(internalCallback);
} else {
throw new IllegalStateException("Telephony service is null.");
}
@@ -2372,7 +2387,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
if (internalCallback != null) {
- telephony.unregisterForCapabilitiesChanged(mSubId, internalCallback);
+ telephony.unregisterForCapabilitiesChanged(internalCallback);
} else {
loge("unregisterForCapabilitiesChanged: No internal callback.");
}
@@ -2448,7 +2463,7 @@
};
sSatelliteSupportedStateCallbackMap.put(callback, internalCallback);
return telephony.registerForSatelliteSupportedStateChanged(
- mSubId, internalCallback);
+ internalCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -2483,7 +2498,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
if (internalCallback != null) {
- telephony.unregisterForSatelliteSupportedStateChanged(mSubId, internalCallback);
+ telephony.unregisterForSatelliteSupportedStateChanged(internalCallback);
} else {
loge("unregisterForSupportedStateChanged: No internal callback.");
}
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 51154e5..8b51321 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -83,7 +83,16 @@
* is enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
* this may also re-enable the cellular modem.
*
+ * Framework might send an enable request to update the enable attributes of an already-started
+ * satellite session. In such cases, modem needs to apply the new enable attrbitues to the
+ * satellite session. Moreover, modem needs to report its current state and signal strength
+ * level to framework right after receiving this request from framework.
+ *
+ * Framework might send a disable request when an enable request is being processed. In such
+ * cases, modem needs to abort the enable request and process the disable request.
+ *
* @param enableAttributes The enable parameters that will be applied to the satellite session
+ * @param resultCallback The callback to receive the error code result of the operation.
*
* Valid result codes returned:
* SatelliteResult:SATELLITE_RESULT_SUCCESS
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/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0c5f30f..e57c207 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2743,7 +2743,6 @@
/**
* Request to enable or disable the satellite modem.
*
- * @param subId The subId of the subscription to enable or disable the satellite modem for.
* @param enableSatellite True to enable the satellite modem and false to disable.
* @param enableDemoMode True if demo mode is enabled and false otherwise. When
* disabling satellite, {@code enableDemoMode} is always considered as
@@ -2755,93 +2754,83 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
+ void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
boolean isEmergency, in IIntegerConsumer callback);
/**
* Request to get whether the satellite modem is enabled.
*
- * @param subId The subId of the subscription to request whether satellite is enabled for.
* @param receiver Result receiver to get the error code of the request and whether the
* satellite modem is enabled.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestIsSatelliteEnabled(int subId, in ResultReceiver receiver);
+ void requestIsSatelliteEnabled(in ResultReceiver receiver);
/**
* Request to get whether the satellite service demo mode is enabled.
*
- * @param subId The subId of the subscription to request whether the satellite demo mode is
- * enabled for.
* @param receiver Result receiver to get the error code of the request and whether the
* satellite demo mode is enabled.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestIsDemoModeEnabled(int subId, in ResultReceiver receiver);
+ void requestIsDemoModeEnabled(in ResultReceiver receiver);
/**
* Request to get whether the satellite service is enabled with emergency mode.
*
- * @param subId The subId of the subscription to request whether the satellite demo mode is
- * enabled for.
* @param receiver Result receiver to get the error code of the request and whether the
* satellite is enabled with emergency mode.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestIsEmergencyModeEnabled(int subId, in ResultReceiver receiver);
+ void requestIsEmergencyModeEnabled(in ResultReceiver receiver);
/**
* Request to get whether the satellite service is supported on the device.
*
- * @param subId The subId of the subscription to check whether satellite is supported for.
* @param receiver Result receiver to get the error code of the request and whether the
* satellite service is supported on the device.
*/
- void requestIsSatelliteSupported(int subId, in ResultReceiver receiver);
+ void requestIsSatelliteSupported(in ResultReceiver receiver);
/**
* Request to get the capabilities of the satellite service.
*
- * @param subId The subId of the subscription to get the capabilities for.
* @param receiver Result receiver to get the error code of the request and the requested
* capabilities of the satellite service.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestSatelliteCapabilities(int subId, in ResultReceiver receiver);
+ void requestSatelliteCapabilities(in ResultReceiver receiver);
/**
* Start receiving satellite transmission updates.
*
- * @param subId The subId of the subscription to stop satellite transmission updates for.
* @param resultCallback The callback to get the result of the request.
* @param callback The callback to handle transmission updates.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void startSatelliteTransmissionUpdates(int subId, in IIntegerConsumer resultCallback,
+ void startSatelliteTransmissionUpdates(in IIntegerConsumer resultCallback,
in ISatelliteTransmissionUpdateCallback callback);
/**
* Stop receiving satellite transmission updates.
*
- * @param subId The subId of the subscritpion to stop satellite transmission updates for.
* @param resultCallback The callback to get the result of the request.
* @param callback The callback that was passed to startSatelliteTransmissionUpdates.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void stopSatelliteTransmissionUpdates(int subId, in IIntegerConsumer resultCallback,
+ void stopSatelliteTransmissionUpdates(in IIntegerConsumer resultCallback,
in ISatelliteTransmissionUpdateCallback callback);
/**
* Register the subscription with a satellite provider.
* This is needed to register the subscription if the provider allows dynamic registration.
*
- * @param subId The subId of the subscription to be provisioned.
* @param token The token to be used as a unique identifier for provisioning with satellite
* gateway.
* @provisionData Data from the provisioning app that can be used by provisioning server
@@ -2851,7 +2840,7 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- ICancellationSignal provisionSatelliteService(int subId, in String token,
+ ICancellationSignal provisionSatelliteService(in String token,
in byte[] provisionData, in IIntegerConsumer callback);
/**
@@ -2861,110 +2850,99 @@
* {@link SatelliteCallback.SatelliteProvisionStateListener#onSatelliteProvisionStateChanged}
* should report as deprovisioned.
*
- * @param subId The subId of the subscription to be deprovisioned.
* @param token The token of the device/subscription to be deprovisioned.
* @param callback The callback to get the result of the request.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void deprovisionSatelliteService(int subId, in String token, in IIntegerConsumer callback);
+ void deprovisionSatelliteService(in String token, in IIntegerConsumer callback);
/**
* Registers for provision state changed from satellite modem.
*
- * @param subId The subId of the subscription to register for provision state changed.
* @param callback The callback to handle the satellite provision state changed event.
*
* @return The {@link SatelliteError} result of the operation.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- int registerForSatelliteProvisionStateChanged(int subId,
- in ISatelliteProvisionStateCallback callback);
+ int registerForSatelliteProvisionStateChanged(in ISatelliteProvisionStateCallback callback);
/**
* Unregisters for provision state changed from satellite modem.
* If callback was not registered before, the request will be ignored.
*
- * @param subId The subId of the subscription to unregister for provision state changed.
* @param callback The callback that was passed to registerForSatelliteProvisionStateChanged.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void unregisterForSatelliteProvisionStateChanged(int subId,
+ void unregisterForSatelliteProvisionStateChanged(
in ISatelliteProvisionStateCallback callback);
/**
* Request to get whether the device is provisioned with a satellite provider.
*
- * @param subId The subId of the subscription to get whether the device is provisioned for.
* @param receiver Result receiver to get the error code of the request and whether the
* device is provisioned with a satellite provider.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestIsSatelliteProvisioned(int subId, in ResultReceiver receiver);
+ void requestIsSatelliteProvisioned(in ResultReceiver receiver);
/**
* Registers for modem state changed from satellite modem.
*
- * @param subId The subId of the subscription to register for satellite modem state changed.
* @param callback The callback to handle the satellite modem state changed event.
*
* @return The {@link SatelliteError} result of the operation.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- int registerForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback);
+ int registerForSatelliteModemStateChanged(ISatelliteModemStateCallback callback);
/**
* Unregisters for modem state changed from satellite modem.
* If callback was not registered before, the request will be ignored.
*
- * @param subId The subId of the subscription to unregister for satellite modem state changed.
* @param callback The callback that was passed to registerForSatelliteStateChanged.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void unregisterForModemStateChanged(int subId, ISatelliteModemStateCallback callback);
+ void unregisterForModemStateChanged(ISatelliteModemStateCallback callback);
/**
* Register to receive incoming datagrams over satellite.
*
- * @param subId The subId of the subscription to register for incoming satellite datagrams.
* @param callback The callback to handle the incoming datagrams.
*
* @return The {@link SatelliteError} result of the operation.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- int registerForIncomingDatagram(int subId, ISatelliteDatagramCallback callback);
+ int registerForIncomingDatagram(ISatelliteDatagramCallback callback);
/**
* Unregister to stop receiving incoming datagrams over satellite.
* If callback was not registered before, the request will be ignored.
*
- * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
* @param callback The callback that was passed to registerForIncomingDatagram.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void unregisterForIncomingDatagram(int subId, ISatelliteDatagramCallback callback);
+ void unregisterForIncomingDatagram(ISatelliteDatagramCallback callback);
/**
* Poll pending satellite datagrams over satellite.
*
- * @param subId The subId of the subscription used for receiving datagrams.
* @param callback The callback to get the result of the request.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void pollPendingDatagrams(int subId, IIntegerConsumer callback);
+ void pollPendingDatagrams(IIntegerConsumer callback);
/**
* Send datagram over satellite.
*
- * @param subId The subId of the subscription to send satellite datagrams for.
* @param datagramType Type of datagram.
* @param datagram Datagram to send over satellite.
* @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
@@ -2973,7 +2951,7 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void sendDatagram(int subId, int datagramType, in SatelliteDatagram datagram,
+ void sendDatagram(int datagramType, in SatelliteDatagram datagram,
in boolean needFullScreenPointingUI, IIntegerConsumer callback);
/**
@@ -2991,13 +2969,12 @@
/**
* Request to get the time after which the satellite will be visible.
*
- * @param subId The subId to get the time after which the satellite will be visible for.
* @param receiver Result receiver to get the error code of the request and the requested
* time after which the satellite will be visible.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestTimeForNextSatelliteVisibility(int subId, in ResultReceiver receiver);
+ void requestTimeForNextSatelliteVisibility(in ResultReceiver receiver);
/**
* Inform whether the device is aligned with the satellite within in margin for demo mode.
@@ -3007,7 +2984,7 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void setDeviceAlignedWithSatellite(int subId, boolean isAligned);
+ void setDeviceAlignedWithSatellite(boolean isAligned);
/**
* This API can be used by only CTS to update satellite vendor service package name.
@@ -3163,20 +3140,18 @@
/**
* Request to get the signal strength of the satellite connection.
*
- * @param subId The subId of the subscription to request for.
* @param receiver Result receiver to get the error code of the request and the current signal
* strength of the satellite connection.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestNtnSignalStrength(int subId, in ResultReceiver receiver);
+ void requestNtnSignalStrength(in ResultReceiver receiver);
/**
* Registers for NTN signal strength changed from satellite modem. If the registration operation
* is not successful, a {@link SatelliteException} that contains {@link SatelliteResult} will be
* thrown.
*
- * @param subId The subId of the subscription to request for.
* @param callback The callback to handle the NTN signal strength changed event. If the
* operation is successful, {@link NtnSignalStrengthCallback#onNtnSignalStrengthChanged(
* NtnSignalStrength)} will return an instance of {@link NtnSignalStrength} with a value of
@@ -3185,30 +3160,27 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void registerForNtnSignalStrengthChanged(int subId,
- in INtnSignalStrengthCallback callback);
+ void registerForNtnSignalStrengthChanged(in INtnSignalStrengthCallback callback);
/**
* Unregisters for NTN signal strength changed from satellite modem.
* If callback was not registered before, the request will be ignored.
*
- * @param subId The subId of the subscription to unregister for provision state changed.
* @param callback The callback that was passed to
* {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void unregisterForNtnSignalStrengthChanged(int subId, in INtnSignalStrengthCallback callback);
+ void unregisterForNtnSignalStrengthChanged(in INtnSignalStrengthCallback callback);
/**
* Registers for satellite capabilities change event from the satellite service.
*
- * @param executor The executor on which the callback will be called.
* @param callback The callback to handle the satellite capabilities changed event.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- int registerForCapabilitiesChanged(int subId, in ISatelliteCapabilitiesCallback callback);
+ int registerForCapabilitiesChanged(in ISatelliteCapabilitiesCallback callback);
/**
* Unregisters for satellite capabilities change event from the satellite service.
@@ -3219,8 +3191,7 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void unregisterForCapabilitiesChanged(int subId,
- in ISatelliteCapabilitiesCallback callback);
+ void unregisterForCapabilitiesChanged(in ISatelliteCapabilitiesCallback callback);
/**
* This API can be used by only CTS to override the cached value for the device overlay config
@@ -3329,26 +3300,24 @@
/**
* Registers for supported state changed from satellite modem.
*
- * @param subId The subId of the subscription to register for supported state changed.
* @param callback The callback to handle the satellite supported state changed event.
*
* @return The {@link SatelliteError} result of the operation.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- int registerForSatelliteSupportedStateChanged(int subId,
+ int registerForSatelliteSupportedStateChanged(
in ISatelliteSupportedStateCallback callback);
/**
* Unregisters for supported state changed from satellite modem.
* If callback was not registered before, the request will be ignored.
*
- * @param subId The subId of the subscription to unregister for supported state changed.
* @param callback The callback that was passed to registerForSatelliteSupportedStateChanged.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void unregisterForSatelliteSupportedStateChanged(int subId,
+ void unregisterForSatelliteSupportedStateChanged(
in ISatelliteSupportedStateCallback callback);
/**
@@ -3425,4 +3394,19 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void provisionSatellite(in List<SatelliteSubscriberInfo> list, in ResultReceiver result);
+
+ /**
+ * This API can be used by only CTS to override the cached value for the device overlay config
+ * value :
+ * config_satellite_gateway_service_package and
+ * config_satellite_carrier_roaming_esos_provisioned_class.
+ * These values are set before sending an intent to broadcast there are any change to list of
+ * subscriber informations.
+ *
+ * @param name the name is one of the following that constitute an intent.
+ * Component package name, or component class name.
+ * @return {@code true} if the setting is successful, {@code false} otherwise.
+ * @hide
+ */
+ boolean setSatelliteSubscriberIdListChangedIntentComponent(in String name);
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 29286e8..b92ba66 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -169,13 +169,13 @@
/**
* Set to false to disable SMS receiving, default is
- * the value of config_sms_capable
+ * the value of TelephonyManager.isDeviceSmsCapable
*/
static final String PROPERTY_SMS_RECEIVE = "telephony.sms.receive";
/**
* Set to false to disable SMS sending, default is
- * the value of config_sms_capable
+ * the value of TelephonyManager.isDeviceSmsCapable
*/
static final String PROPERTY_SMS_SEND = "telephony.sms.send";
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 379b45c..c3e1a1f 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -24,6 +24,7 @@
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.traces.parsers.toFlickerComponent
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -177,6 +178,13 @@
@Ignore("Not applicable to this CUJ.")
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
+ @FlakyTest(bugId = 342596801)
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ @FlakyTest(bugId = 342596801)
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
/** {@inheritDoc} */
private var startDisplayBounds = Rect()
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index adeba72..6e6a327 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -196,7 +196,7 @@
super.appLayerBecomesVisible()
}
- @Presubmit
+ @FlakyTest(bugId = 338296297)
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 753cb1f..3f6a0bf 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -48,6 +48,13 @@
RIGHT_BOTTOM
}
+ enum class Edges {
+ LEFT,
+ RIGHT,
+ TOP,
+ BOTTOM
+ }
+
/** Wait for an app moved to desktop to finish its transition. */
private fun waitForAppToMoveToDesktop(wmHelper: WindowManagerStateHelper) {
wmHelper
@@ -124,7 +131,8 @@
val displayRect = getDisplayRect(wmHelper)
val insets = getWindowInsets(
- context, WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
+ context, WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()
+ )
displayRect.inset(insets)
val expectedWidth = displayRect.width() / 2
@@ -187,6 +195,40 @@
dragWindow(startX, startY, endX, endY, wmHelper, device)
}
+ /** Resize a desktop app from its edges. */
+ fun edgeResize(
+ wmHelper: WindowManagerStateHelper,
+ motionEvent: MotionEventHelper,
+ edge: Edges
+ ) {
+ val windowRect = wmHelper.getWindowRegion(innerHelper).bounds
+ val (startX, startY) = getStartCoordinatesForEdgeResize(windowRect, edge)
+ val verticalChange = when (edge) {
+ Edges.LEFT -> 0
+ Edges.RIGHT -> 0
+ Edges.TOP -> -100
+ Edges.BOTTOM -> 100
+ }
+ val horizontalChange = when (edge) {
+ Edges.LEFT -> -100
+ Edges.RIGHT -> 100
+ Edges.TOP -> 0
+ Edges.BOTTOM -> 0
+ }
+
+ // The position we want to drag to
+ val endY = startY + verticalChange
+ val endX = startX + horizontalChange
+
+ motionEvent.actionDown(startX, startY)
+ motionEvent.actionMove(startX, startY, endX, endY, /* steps= */100)
+ motionEvent.actionUp(endX, endY)
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .waitForAndVerify()
+ }
+
/** Drag a window from a source coordinate to a destination coordinate. */
fun dragWindow(
startX: Int, startY: Int,
@@ -237,6 +279,18 @@
}
}
+ private fun getStartCoordinatesForEdgeResize(
+ windowRect: Rect,
+ edge: Edges
+ ): Pair<Int, Int> {
+ return when (edge) {
+ Edges.LEFT -> Pair(windowRect.left, windowRect.bottom / 2)
+ Edges.RIGHT -> Pair(windowRect.right, windowRect.bottom / 2)
+ Edges.TOP -> Pair(windowRect.right / 2, windowRect.top)
+ Edges.BOTTOM -> Pair(windowRect.right / 2, windowRect.bottom)
+ }
+ }
+
/** Exit desktop mode by dragging the app handle to the top drag zone. */
fun exitDesktopWithDragToTopDragZone(
wmHelper: WindowManagerStateHelper,
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
new file mode 100644
index 0000000..0835398
--- /dev/null
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.os.SystemClock
+import android.view.ContentInfo.Source
+import android.view.InputDevice.SOURCE_MOUSE
+import android.view.InputDevice.SOURCE_STYLUS
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
+import android.view.MotionEvent.TOOL_TYPE_FINGER
+import android.view.MotionEvent.TOOL_TYPE_MOUSE
+import android.view.MotionEvent.TOOL_TYPE_STYLUS
+import android.view.MotionEvent.ToolType
+
+/**
+ * Helper class for injecting a custom motion event and performing some actions. This is used for
+ * instrumenting input injections like stylus, mouse and touchpad.
+ */
+class MotionEventHelper(
+ private val instr: Instrumentation,
+ private val inputMethod: InputMethod
+) {
+ enum class InputMethod(@ToolType val toolType: Int, @Source val source: Int) {
+ STYLUS(TOOL_TYPE_STYLUS, SOURCE_STYLUS),
+ MOUSE(TOOL_TYPE_MOUSE, SOURCE_MOUSE),
+ TOUCHPAD(TOOL_TYPE_FINGER, SOURCE_MOUSE)
+ }
+
+ fun actionDown(x: Int, y: Int) {
+ injectMotionEvent(ACTION_DOWN, x, y)
+ }
+
+ fun actionUp(x: Int, y: Int) {
+ injectMotionEvent(ACTION_UP, x, y)
+ }
+
+ fun actionMove(startX: Int, startY: Int, endX: Int, endY: Int, steps: Int) {
+ val incrementX = (endX - startX).toFloat() / (steps - 1)
+ val incrementY = (endY - startY).toFloat() / (steps - 1)
+
+ for (i in 0..steps) {
+ val time = SystemClock.uptimeMillis()
+ val x = startX + incrementX * i
+ val y = startY + incrementY * i
+
+ val moveEvent = getMotionEvent(time, time, ACTION_MOVE, x, y)
+ injectMotionEvent(moveEvent)
+ }
+ }
+
+ private fun injectMotionEvent(action: Int, x: Int, y: Int): MotionEvent {
+ val eventTime = SystemClock.uptimeMillis()
+ val event = getMotionEvent(eventTime, eventTime, action, x.toFloat(), y.toFloat())
+ injectMotionEvent(event)
+ return event
+ }
+
+ private fun injectMotionEvent(event: MotionEvent) {
+ instr.uiAutomation.injectInputEvent(event, true, false)
+ }
+
+ private fun getMotionEvent(
+ downTime: Long,
+ eventTime: Long,
+ action: Int,
+ x: Float,
+ y: Float,
+ ): MotionEvent {
+ val properties = MotionEvent.PointerProperties.createArray(1)
+ properties[0].toolType = inputMethod.toolType
+ properties[0].id = 1
+
+ val coords = MotionEvent.PointerCoords.createArray(1)
+ coords[0].x = x
+ coords[0].y = y
+ coords[0].pressure = 1f
+
+ val event =
+ MotionEvent.obtain(
+ downTime,
+ eventTime,
+ action,
+ /* pointerCount= */ 1,
+ properties,
+ coords,
+ /* metaState= */ 0,
+ /* buttonState= */ 0,
+ /* xPrecision = */ 1f,
+ /* yPrecision = */ 1f,
+ /* deviceId = */ 0,
+ /* edgeFlags = */ 0,
+ inputMethod.source,
+ /* flags = */ 0
+ )
+ event.displayId = 0
+ return event
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 43fd57b..931e4f8 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -269,9 +269,23 @@
/** Expand the PIP window back to full screen via intent and wait until the app is visible */
fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntent(wmHelper)
- fun changeAspectRatio() {
+ fun changeAspectRatio(wmHelper: WindowManagerStateHelper) {
val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO")
context.sendBroadcast(intent)
+ // Wait on WMHelper on size change upon aspect ratio change
+ val windowRect = getWindowRect(wmHelper)
+ wmHelper
+ .StateSyncBuilder()
+ .add("pipAspectRatioChanged") {
+ val pipAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ this.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val pipRegion = pipAppWindow.frameRegion
+ return@add pipRegion != Region(windowRect)
+ }
+ .waitForAndVerify()
}
fun clickEnterPipButton(wmHelper: WindowManagerStateHelper) {
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
index ef75d4d..93254f7 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
@@ -71,7 +71,7 @@
windowInsetsController.setSystemBarsBehavior(
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
);
- // Hide both the status bar and the navigation bar.
- windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());
+ // Hide status bar only to avoid flakiness on gesture quick switch cases.
+ windowInsetsController.hide(WindowInsetsCompat.Type.statusBars());
}
}
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index cf42a34..f3e040a 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -40,7 +40,7 @@
"frameworks-base-testutils",
"hamcrest-library",
"kotlin-test",
- "mockito-target-minus-junit4",
+ "mockito-target-extended-minus-junit4",
"platform-test-annotations",
"platform-screenshot-diff-core",
"services.core.unboosted",
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java
new file mode 100644
index 0000000..5875520
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 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.server.input.debug;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.input.InputManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.InputDevice;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.input.InputManagerService;
+import com.android.server.input.TouchpadHardwareProperties;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Build/Install/Run:
+ * atest TouchpadDebugViewControllerTests
+ */
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class TouchpadDebugViewControllerTests {
+ private static final int DEVICE_ID = 1000;
+ private static final String TAG = "TouchpadDebugViewController";
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private Context mContext;
+ private TouchpadDebugViewController mTouchpadDebugViewController;
+ @Mock
+ private InputManager mInputManagerMock;
+ @Mock
+ private InputManagerService mInputManagerServiceMock;
+ @Mock
+ private WindowManager mWindowManagerMock;
+ private TestableLooper mTestableLooper;
+
+ @Before
+ public void setup() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ TestableContext mTestableContext = new TestableContext(mContext);
+ mTestableContext.addMockSystemService(WindowManager.class, mWindowManagerMock);
+
+ Rect bounds = new Rect(0, 0, 2560, 1600);
+ WindowMetrics metrics = new WindowMetrics(bounds, new WindowInsets(bounds), 1.0f);
+
+ when(mWindowManagerMock.getCurrentWindowMetrics()).thenReturn(metrics);
+
+ unMockTouchpad();
+
+ mTestableLooper = TestableLooper.get(this);
+
+ mTestableContext.addMockSystemService(InputManager.class, mInputManagerMock);
+ when(mInputManagerServiceMock.getTouchpadHardwareProperties(DEVICE_ID)).thenReturn(
+ new TouchpadHardwareProperties.Builder(-100f, 100f, -100f, 100f, 45f, 45f, -5f, 5f,
+ (short) 10, true, false).build());
+
+ mTouchpadDebugViewController = new TouchpadDebugViewController(mTestableContext,
+ mTestableLooper.getLooper(), mInputManagerServiceMock);
+ }
+
+ private InputDevice createTouchpadInputDevice(int id) {
+ return new InputDevice.Builder()
+ .setId(id)
+ .setSources(InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)
+ .setName("Test Device " + id)
+ .build();
+ }
+
+ private void mockTouchpad() {
+ when(mInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+ when(mInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+ createTouchpadInputDevice(DEVICE_ID));
+ }
+
+ private void unMockTouchpad() {
+ when(mInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{});
+ when(mInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(null);
+ }
+
+ @Test
+ public void touchpadConnectedWhileSettingDisabled() throws Exception {
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+ mockTouchpad();
+ mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+ verify(mWindowManagerMock, never()).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+ }
+
+ @Test
+ public void settingEnabledWhileNoTouchpadConnected() throws Exception {
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+ verify(mWindowManagerMock, never()).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+ }
+
+ @Test
+ public void touchpadConnectedWhileSettingEnabled() throws Exception {
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+ mockTouchpad();
+ mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+ verify(mWindowManagerMock, times(1)).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+ }
+
+ @Test
+ public void touchpadConnectedWhileSettingEnabledThenDisabled() throws Exception {
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+ mockTouchpad();
+ mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+ verify(mWindowManagerMock, times(1)).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+ verify(mWindowManagerMock, times(1)).addView(any(), any());
+ verify(mWindowManagerMock, times(1)).removeView(any());
+ }
+
+ @Test
+ public void touchpadConnectedWhileSettingDisabledThenEnabled() throws Exception {
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+ mockTouchpad();
+ mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+ verify(mWindowManagerMock, never()).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+ verify(mWindowManagerMock, times(1)).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+ }
+
+ @Test
+ public void touchpadConnectedWhileSettingDisabledThenTouchpadDisconnected() throws Exception {
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+ mockTouchpad();
+ mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+ verify(mWindowManagerMock, never()).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+
+ unMockTouchpad();
+ mTouchpadDebugViewController.onInputDeviceRemoved(DEVICE_ID);
+
+ verify(mWindowManagerMock, never()).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+ }
+
+ @Test
+ public void touchpadConnectedWhileSettingEnabledThenTouchpadDisconnectedThenSettingDisabled()
+ throws Exception {
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+ mockTouchpad();
+ mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+ verify(mWindowManagerMock, times(1)).addView(any(), any());
+ verify(mWindowManagerMock, never()).removeView(any());
+
+ unMockTouchpad();
+ mTouchpadDebugViewController.onInputDeviceRemoved(DEVICE_ID);
+
+ verify(mWindowManagerMock, times(1)).addView(any(), any());
+ verify(mWindowManagerMock, times(1)).removeView(any());
+
+ mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+ verify(mWindowManagerMock, times(1)).addView(any(), any());
+ verify(mWindowManagerMock, times(1)).removeView(any());
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
new file mode 100644
index 0000000..99e04cc
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 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.server.input.debug;
+
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.testing.TestableContext;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.cts.input.MotionEventBuilder;
+import com.android.cts.input.PointerBuilder;
+import com.android.server.input.TouchpadFingerState;
+import com.android.server.input.TouchpadHardwareProperties;
+import com.android.server.input.TouchpadHardwareState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Build/Install/Run:
+ * atest TouchpadDebugViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class TouchpadDebugViewTest {
+ private static final int TOUCHPAD_DEVICE_ID = 6;
+
+ private TouchpadDebugView mTouchpadDebugView;
+ private WindowManager.LayoutParams mWindowLayoutParams;
+
+ @Mock
+ WindowManager mWindowManager;
+
+ Rect mWindowBounds;
+ WindowMetrics mWindowMetrics;
+ TestableContext mTestableContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ mTestableContext = new TestableContext(context);
+
+ mTestableContext.addMockSystemService(WindowManager.class, mWindowManager);
+
+ mWindowBounds = new Rect(0, 0, 2560, 1600);
+ mWindowMetrics = new WindowMetrics(mWindowBounds, new WindowInsets(mWindowBounds), 1.0f);
+
+ when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+
+ mTouchpadDebugView = new TouchpadDebugView(mTestableContext, TOUCHPAD_DEVICE_ID,
+ new TouchpadHardwareProperties.Builder(500f, 500f, 500f,
+ 500f, 0f, 0f, -5f, 5f, (short) 10, true,
+ true).build());
+
+ mTouchpadDebugView.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ );
+
+ doAnswer(invocation -> {
+ mTouchpadDebugView.layout(0, 0, mTouchpadDebugView.getMeasuredWidth(),
+ mTouchpadDebugView.getMeasuredHeight());
+ return null;
+ }).when(mWindowManager).addView(any(), any());
+
+ doAnswer(invocation -> {
+ mTouchpadDebugView.layout(0, 0, mTouchpadDebugView.getMeasuredWidth(),
+ mTouchpadDebugView.getMeasuredHeight());
+ return null;
+ }).when(mWindowManager).updateViewLayout(any(), any());
+
+ mWindowLayoutParams = mTouchpadDebugView.getWindowLayoutParams();
+ mWindowLayoutParams.x = 20;
+ mWindowLayoutParams.y = 20;
+
+ mTouchpadDebugView.layout(0, 0, mTouchpadDebugView.getMeasuredWidth(),
+ mTouchpadDebugView.getMeasuredHeight());
+ }
+
+ @Test
+ public void testDragView() {
+ // Initial view position relative to screen.
+ int initialX = mWindowLayoutParams.x;
+ int initialY = mWindowLayoutParams.y;
+
+ float offsetX = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() + 10;
+ float offsetY = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() + 10;
+
+ // Simulate ACTION_DOWN event (initial touch).
+ MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(40f)
+ .y(40f)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+ verify(mWindowManager, times(0)).updateViewLayout(any(), any());
+
+ // Simulate ACTION_MOVE event (dragging to the right).
+ MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(40f + offsetX)
+ .y(40f + offsetY)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+ ArgumentCaptor<WindowManager.LayoutParams> mWindowLayoutParamsCaptor =
+ ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
+ verify(mWindowManager).updateViewLayout(any(), mWindowLayoutParamsCaptor.capture());
+
+ // Verify position after ACTION_MOVE
+ assertEquals(initialX + (long) offsetX, mWindowLayoutParamsCaptor.getValue().x);
+ assertEquals(initialY + (long) offsetY, mWindowLayoutParamsCaptor.getValue().y);
+
+ // Simulate ACTION_UP event (release touch).
+ MotionEvent actionUp = new MotionEventBuilder(MotionEvent.ACTION_UP, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(40f + offsetX)
+ .y(40f + offsetY)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionUp);
+
+ assertEquals(initialX + (long) offsetX, mWindowLayoutParamsCaptor.getValue().x);
+ assertEquals(initialY + (long) offsetY, mWindowLayoutParamsCaptor.getValue().y);
+ }
+
+ @Test
+ public void testDragViewOutOfBounds() {
+ int initialX = mWindowLayoutParams.x;
+ int initialY = mWindowLayoutParams.y;
+
+ MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(initialX + 10f)
+ .y(initialY + 10f)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+ verify(mWindowManager, times(0)).updateViewLayout(any(), any());
+
+ // Simulate ACTION_MOVE event (dragging far to the right and bottom, beyond screen bounds)
+ MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(mWindowBounds.width() + mTouchpadDebugView.getWidth())
+ .y(mWindowBounds.height() + mTouchpadDebugView.getHeight())
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+ ArgumentCaptor<WindowManager.LayoutParams> mWindowLayoutParamsCaptor =
+ ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
+ verify(mWindowManager).updateViewLayout(any(), mWindowLayoutParamsCaptor.capture());
+
+ // Verify the view has been clamped to the right and bottom edges of the screen
+ assertEquals(mWindowBounds.width() - mTouchpadDebugView.getWidth(),
+ mWindowLayoutParamsCaptor.getValue().x);
+ assertEquals(mWindowBounds.height() - mTouchpadDebugView.getHeight(),
+ mWindowLayoutParamsCaptor.getValue().y);
+
+ MotionEvent actionUp = new MotionEventBuilder(MotionEvent.ACTION_UP, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(mWindowBounds.width() + mTouchpadDebugView.getWidth())
+ .y(mWindowBounds.height() + mTouchpadDebugView.getHeight())
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionUp);
+
+ // Verify the view has been clamped to the right and bottom edges of the screen
+ assertEquals(mWindowBounds.width() - mTouchpadDebugView.getWidth(),
+ mWindowLayoutParamsCaptor.getValue().x);
+ assertEquals(mWindowBounds.height() - mTouchpadDebugView.getHeight(),
+ mWindowLayoutParamsCaptor.getValue().y);
+ }
+
+ @Test
+ public void testSlopOffset() {
+ int initialX = mWindowLayoutParams.x;
+ int initialY = mWindowLayoutParams.y;
+
+ float offsetX = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() / 2.0f;
+ float offsetY = -(ViewConfiguration.get(mTestableContext).getScaledTouchSlop() / 2.0f);
+
+ MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(initialX)
+ .y(initialY)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+ MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(initialX + offsetX)
+ .y(initialY + offsetY)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+ MotionEvent actionUp = new MotionEventBuilder(MotionEvent.ACTION_UP, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(initialX)
+ .y(initialY)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionUp);
+
+ // In this case the updateViewLayout() method wouldn't be called because the drag
+ // distance hasn't exceeded the slop
+ verify(mWindowManager, times(0)).updateViewLayout(any(), any());
+ }
+
+ @Test
+ public void testViewReturnsToInitialPositionOnCancel() {
+ int initialX = mWindowLayoutParams.x;
+ int initialY = mWindowLayoutParams.y;
+
+ float offsetX = 50;
+ float offsetY = 50;
+
+ MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(initialX)
+ .y(initialY)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+ MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(initialX + offsetX)
+ .y(initialY + offsetY)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+ ArgumentCaptor<WindowManager.LayoutParams> mWindowLayoutParamsCaptor =
+ ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
+ verify(mWindowManager).updateViewLayout(any(), mWindowLayoutParamsCaptor.capture());
+
+ assertEquals(initialX + (long) offsetX, mWindowLayoutParamsCaptor.getValue().x);
+ assertEquals(initialY + (long) offsetY, mWindowLayoutParamsCaptor.getValue().y);
+
+ // Simulate ACTION_CANCEL event (canceling the touch event stream)
+ MotionEvent actionCancel = new MotionEventBuilder(MotionEvent.ACTION_CANCEL,
+ SOURCE_TOUCHSCREEN)
+ .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+ .x(initialX + offsetX)
+ .y(initialY + offsetY)
+ )
+ .build();
+ mTouchpadDebugView.dispatchTouchEvent(actionCancel);
+
+ // Verify the view returns to its initial position
+ verify(mWindowManager, times(2)).updateViewLayout(any(),
+ mWindowLayoutParamsCaptor.capture());
+ assertEquals(initialX, mWindowLayoutParamsCaptor.getValue().x);
+ assertEquals(initialY, mWindowLayoutParamsCaptor.getValue().y);
+ }
+
+ @Test
+ public void testTouchpadClick() {
+ View child = mTouchpadDebugView.getChildAt(0);
+
+ mTouchpadDebugView.updateHardwareState(
+ new TouchpadHardwareState(0, 1 /* buttonsDown */, 0, 0,
+ new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
+
+ assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
+
+ mTouchpadDebugView.updateHardwareState(
+ new TouchpadHardwareState(0, 0 /* buttonsDown */, 0, 0,
+ new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
+
+ assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.RED);
+
+ mTouchpadDebugView.updateHardwareState(
+ new TouchpadHardwareState(0, 1 /* buttonsDown */, 0, 0,
+ new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
+
+ assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
+
+ // Color should not change because hardware state of a different touchpad
+ mTouchpadDebugView.updateHardwareState(
+ new TouchpadHardwareState(0, 0 /* buttonsDown */, 0, 0,
+ new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID + 1);
+
+ assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index 6db5f82..e841d9e 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -16,8 +16,6 @@
package com.android.internal.protolog;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -30,7 +28,6 @@
import static java.io.File.createTempFile;
-import android.content.Context;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.tools.ScenarioBuilder;
@@ -45,6 +42,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.protolog.ProtoLogConfigurationService.ViewerConfigFileTracer;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogDataType;
import com.android.internal.protolog.common.LogLevel;
@@ -53,11 +51,11 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
import perfetto.protos.Protolog;
import perfetto.protos.ProtologCommon;
@@ -75,6 +73,8 @@
@Presubmit
@RunWith(JUnit4.class)
public class PerfettoProtoLogImplTest {
+ private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog";
+ private static final String MOCK_VIEWER_CONFIG_FILE = "my/mock/viewer/config/file.pb";
private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation()
.getTargetContext().getFilesDir();
@@ -91,28 +91,19 @@
new TraceConfig(false, true, false)
);
- private PerfettoProtoLogImpl mProtoLog;
- private Protolog.ProtoLogViewerConfig.Builder mViewerConfigBuilder;
- private File mFile;
- private Runnable mCacheUpdater;
+ private static ProtoLogConfigurationService sProtoLogConfigurationService;
+ private static PerfettoProtoLogImpl sProtoLog;
+ private static Protolog.ProtoLogViewerConfig.Builder sViewerConfigBuilder;
+ private static Runnable sCacheUpdater;
- private ProtoLogViewerConfigReader mReader;
+ private static ProtoLogViewerConfigReader sReader;
public PerfettoProtoLogImplTest() throws IOException {
}
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- final Context testContext = getInstrumentation().getContext();
- mFile = testContext.getFileStreamPath("tracing_test.dat");
- //noinspection ResultOfMethodCallIgnored
- mFile.delete();
-
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
-
- mViewerConfigBuilder = Protolog.ProtoLogViewerConfig.newBuilder()
+ @BeforeClass
+ public static void setUp() throws Exception {
+ sViewerConfigBuilder = Protolog.ProtoLogViewerConfig.newBuilder()
.addGroups(
Protolog.ProtoLogViewerConfig.Group.newBuilder()
.setId(1)
@@ -158,36 +149,62 @@
ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock(
ViewerConfigInputStreamProvider.class);
Mockito.when(viewerConfigInputStreamProvider.getInputStream())
- .thenAnswer(it -> new ProtoInputStream(mViewerConfigBuilder.build().toByteArray()));
+ .thenAnswer(it -> new ProtoInputStream(sViewerConfigBuilder.build().toByteArray()));
- mCacheUpdater = () -> {};
- mReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
- mProtoLog = new PerfettoProtoLogImpl(
- viewerConfigInputStreamProvider, mReader,
- () -> mCacheUpdater.run(), TestProtoLogGroup.values());
+ sCacheUpdater = () -> {};
+ sReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
+
+ final ProtoLogDataSourceBuilder dataSourceBuilder =
+ (onStart, onFlush, onStop) -> new ProtoLogDataSource(
+ onStart, onFlush, onStop, TEST_PROTOLOG_DATASOURCE_NAME);
+ final ViewerConfigFileTracer tracer = (dataSource, viewerConfigFilePath) -> {
+ Utils.dumpViewerConfig(dataSource, () -> {
+ if (!viewerConfigFilePath.equals(MOCK_VIEWER_CONFIG_FILE)) {
+ throw new RuntimeException(
+ "Unexpected viewer config file path provided");
+ }
+ return new ProtoInputStream(sViewerConfigBuilder.build().toByteArray());
+ });
+ };
+ sProtoLogConfigurationService = new ProtoLogConfigurationService(dataSourceBuilder, tracer);
+
+ if (android.tracing.Flags.clientSideProtoLogging()) {
+ sProtoLog = new PerfettoProtoLogImpl(
+ MOCK_VIEWER_CONFIG_FILE, sReader, () -> sCacheUpdater.run(),
+ TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService);
+ } else {
+ sProtoLog = new PerfettoProtoLogImpl(
+ viewerConfigInputStreamProvider, sReader, () -> sCacheUpdater.run(),
+ TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService);
+ }
+ }
+
+ @Before
+ public void before() {
+ Mockito.reset(sReader);
+
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
}
@After
public void tearDown() {
- if (mFile != null) {
- //noinspection ResultOfMethodCallIgnored
- mFile.delete();
- }
ProtoLogImpl.setSingleInstance(null);
}
@Test
public void isEnabled_returnsFalseByDefault() {
- assertFalse(mProtoLog.isProtoEnabled());
+ assertFalse(sProtoLog.isProtoEnabled());
}
@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());
+ assertTrue(sProtoLog.isProtoEnabled());
} finally {
traceMonitor.stop(mWriter);
}
@@ -195,35 +212,37 @@
@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());
+ assertTrue(sProtoLog.isProtoEnabled());
} finally {
traceMonitor.stop(mWriter);
}
- assertFalse(mProtoLog.isProtoEnabled());
+ assertFalse(sProtoLog.isProtoEnabled());
}
@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
// override.
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -238,22 +257,24 @@
@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,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -273,21 +294,23 @@
@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,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -304,20 +327,20 @@
@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,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -336,8 +359,8 @@
@Test
public void log_logcatEnabled() {
- when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f");
- PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ when(sReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
@@ -348,13 +371,13 @@
verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
LogLevel.INFO),
eq("test true 10000 % 0x7530 test 3.0E-6"));
- verify(mReader).getViewerString(eq(1234L));
+ verify(sReader).getViewerString(eq(1234L));
}
@Test
public void log_logcatEnabledInvalidMessage() {
- when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f");
- PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ when(sReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
@@ -366,29 +389,28 @@
LogLevel.INFO),
eq("FORMAT_ERROR \"test %b %d %% %x %s %f\", "
+ "args=(true, 10000, 1.0E-4, 2.0E-5, test)"));
- verify(mReader).getViewerString(eq(1234L));
+ verify(sReader).getViewerString(eq(1234L));
}
@Test
public void log_logcatEnabledNoMessage() {
- when(mReader.getViewerString(anyLong())).thenReturn(null);
- PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ when(sReader.getViewerString(anyLong())).thenReturn(null);
+ PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
- implSpy.log(
- LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321,
+ implSpy.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321,
new Object[]{5});
verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)"));
- verify(mReader).getViewerString(eq(1234L));
+ verify(sReader).getViewerString(eq(1234L));
}
@Test
public void log_logcatDisabled() {
- when(mReader.getViewerString(anyLong())).thenReturn("test %d");
- PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ when(sReader.getViewerString(anyLong())).thenReturn("test %d");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
implSpy.log(
@@ -396,7 +418,7 @@
new Object[]{5});
verify(implSpy, never()).passToLogcat(any(), any(), any());
- verify(mReader, never()).getViewerString(anyLong());
+ verify(sReader, never()).getViewerString(anyLong());
}
@Test
@@ -405,16 +427,18 @@
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 {
+ assertFalse(sProtoLog.isProtoEnabled());
traceMonitor.start();
- assertTrue(mProtoLog.isProtoEnabled());
+ assertTrue(sProtoLog.isProtoEnabled());
before = SystemClock.elapsedRealtimeNanos();
- mProtoLog.log(
+ sProtoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash,
0b1110101001010100,
new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
@@ -432,21 +456,23 @@
Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
.isAtMost(after);
Truth.assertThat(protolog.messages.getFirst().getMessage())
- .isEqualTo("My test message :: test, 2, 4, 6, 0.400000, 5.000000e-01, 0.6, true");
+ .isEqualTo(
+ "My test message :: test, 1, 2, 3, 0.400000, 5.000000e-01, 0.6, true");
}
@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 {
traceMonitor.start();
- assertTrue(mProtoLog.isProtoEnabled());
+ assertTrue(sProtoLog.isProtoEnabled());
before = SystemClock.elapsedRealtimeNanos();
- mProtoLog.log(
+ sProtoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP,
"My test message :: %s, %d, %x, %f, %b",
"test", 1, 3, 0.4, true);
@@ -464,16 +490,17 @@
Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
.isAtMost(after);
Truth.assertThat(protolog.messages.getFirst().getMessage())
- .isEqualTo("My test message :: test, 2, 6, 0.400000, true");
+ .isEqualTo("My test message :: test, 1, 3, 0.400000, true");
}
@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,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -489,7 +516,7 @@
private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) {
final long messageId = new Random().nextLong();
- mViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
+ sViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
.setMessageId(messageId)
.setMessage(message)
.setLevel(logLevel)
@@ -504,14 +531,15 @@
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 {
traceMonitor.start();
before = SystemClock.elapsedRealtimeNanos();
- mProtoLog.log(
+ sProtoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash,
0b01100100,
new Object[]{"test", 1, 0.1, true});
@@ -526,11 +554,12 @@
@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,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
0b11, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -544,16 +573,18 @@
@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();
- ProtoLogImpl.setSingleInstance(mProtoLog);
+ ProtoLogImpl.setSingleInstance(sProtoLog);
ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1,
0b11, true);
} finally {
@@ -577,20 +608,20 @@
@Test
public void cacheIsUpdatedWhenTracesStartAndStop() {
final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0);
- mCacheUpdater = cacheUpdateCallCount::incrementAndGet;
+ sCacheUpdater = 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);
@@ -619,107 +650,107 @@
@Test
public void isEnabledUpdatesBasedOnRunningTraces() {
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)).isFalse();
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)).isFalse();
PerfettoTraceMonitor traceMonitor1 =
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 {
traceMonitor1.start();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
.isTrue();
try {
traceMonitor2.start();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP,
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP,
LogLevel.VERBOSE)).isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
.isTrue();
} finally {
traceMonitor2.stop(mWriter);
}
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
.isTrue();
} finally {
traceMonitor1.stop(mWriter);
}
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
.isFalse();
}
@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();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
"My test null string: %s", (Object) null);
} finally {
traceMonitor.stop(mWriter);
@@ -735,14 +766,14 @@
@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();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
"My null args: %d, %f, %b", null, null, null);
} finally {
traceMonitor.stop(mWriter);
@@ -753,18 +784,18 @@
Truth.assertThat(protolog.messages).hasSize(1);
Truth.assertThat(protolog.messages.get(0).getMessage())
- .isEqualTo("My null args: 0, 0, false");
+ .isEqualTo("My null args: 0, 0.000000, false");
}
@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()
@@ -776,7 +807,7 @@
traceMonitor1.start();
traceMonitor2.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor1.stop(mWriter);
@@ -800,16 +831,17 @@
@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,
- "This message should not be logged");
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP,
- "This message should logged %d", 123);
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP,
- "This message should also be logged %d", 567);
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
+ "This message should not be logged");
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP,
+ "This message should be logged %d", 123);
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP,
+ "This message should also be logged %d", 567);
} finally {
traceMonitor.stop(mWriter);
}
@@ -822,7 +854,7 @@
Truth.assertThat(protolog.messages.get(0).getLevel())
.isEqualTo(LogLevel.WARN);
Truth.assertThat(protolog.messages.get(0).getMessage())
- .isEqualTo("This message should logged 123");
+ .isEqualTo("This message should be logged 123");
Truth.assertThat(protolog.messages.get(1).getLevel())
.isEqualTo(LogLevel.ERROR);
diff --git a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
index 8c16079..01f8bc1 100644
--- a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
+++ b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
@@ -16,33 +16,26 @@
package com.android.tests.rollback.host;
+import static com.google.common.truth.Truth.assertThat;
+
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Truth;
-import static com.google.common.truth.Truth.assertThat;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class WatchdogEventLogger {
- private static final String[] ROLLBACK_EVENT_TYPES = {
- "ROLLBACK_INITIATE", "ROLLBACK_BOOT_TRIGGERED", "ROLLBACK_SUCCESS"};
- private static final String[] ROLLBACK_EVENT_ATTRS = {
- "logPackage", "rollbackReason", "failedPackageName"};
- private static final String PROP_PREFIX = "persist.sys.rollbacktest.";
private ITestDevice mDevice;
- private void resetProperties(boolean enabled) throws Exception {
+ private void updateTestSysProp(boolean enabled) throws Exception {
try {
mDevice.enableAdbRoot();
assertThat(mDevice.setProperty(
- PROP_PREFIX + "enabled", String.valueOf(enabled))).isTrue();
- for (String type : ROLLBACK_EVENT_TYPES) {
- String key = PROP_PREFIX + type;
- assertThat(mDevice.setProperty(key, "")).isTrue();
- for (String attr : ROLLBACK_EVENT_ATTRS) {
- assertThat(mDevice.setProperty(key + "." + attr, "")).isTrue();
- }
- }
+ "persist.sys.rollbacktest.enabled", String.valueOf(enabled))).isTrue();
} finally {
mDevice.disableAdbRoot();
}
@@ -50,19 +43,17 @@
public void start(ITestDevice device) throws Exception {
mDevice = device;
- resetProperties(true);
+ updateTestSysProp(true);
}
public void stop() throws Exception {
if (mDevice != null) {
- resetProperties(false);
+ updateTestSysProp(false);
}
}
- private boolean matchProperty(String type, String attr, String expectedVal) throws Exception {
- String key = PROP_PREFIX + type + "." + attr;
- String val = mDevice.getProperty(key);
- return expectedVal == null || expectedVal.equals(val);
+ private boolean verifyEventContainsVal(String watchdogEvent, String expectedVal) {
+ return expectedVal == null || watchdogEvent.contains(expectedVal);
}
/**
@@ -72,11 +63,33 @@
* occurred, and return {@code true} if an event exists which matches all criteria.
*/
public boolean watchdogEventOccurred(String type, String logPackage,
- String rollbackReason, String failedPackageName) throws Exception {
- return mDevice.getBooleanProperty(PROP_PREFIX + type, false)
- && matchProperty(type, "logPackage", logPackage)
- && matchProperty(type, "rollbackReason", rollbackReason)
- && matchProperty(type, "failedPackageName", failedPackageName);
+ String rollbackReason, String failedPackageName) {
+ String watchdogEvent = getEventForRollbackType(type);
+ return (watchdogEvent != null)
+ && verifyEventContainsVal(watchdogEvent, logPackage)
+ && verifyEventContainsVal(watchdogEvent, rollbackReason)
+ && verifyEventContainsVal(watchdogEvent, failedPackageName);
+ }
+
+ /** Returns last matched event for rollbackType **/
+ private String getEventForRollbackType(String rollbackType) {
+ String lastMatchedEvent = null;
+ try {
+ String rollbackDump = mDevice.executeShellCommand("dumpsys rollback");
+ String eventRegex = ".*%s%s(.*)\\n";
+ String eventPrefix = "Watchdog event occurred with type: ";
+
+ final Pattern pattern = Pattern.compile(
+ String.format(eventRegex, eventPrefix, rollbackType));
+ final Matcher matcher = pattern.matcher(rollbackDump);
+ while (matcher.find()) {
+ lastMatchedEvent = matcher.group(1);
+ }
+ CLog.d("Found watchdogEvent: " + lastMatchedEvent + " for type: " + rollbackType);
+ } catch (Exception e) {
+ CLog.e("Unable to find event for type: " + rollbackType, e);
+ }
+ return lastMatchedEvent;
}
static class Subject extends com.google.common.truth.Subject {
@@ -97,7 +110,7 @@
}
void eventOccurred(String type, String logPackage, String rollbackReason,
- String failedPackageName) throws Exception {
+ String failedPackageName) {
check("watchdogEventOccurred(type=%s, logPackage=%s, rollbackReason=%s, "
+ "failedPackageName=%s)", type, logPackage, rollbackReason, failedPackageName)
.that(mActual.watchdogEventOccurred(type, logPackage, rollbackReason,
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
index 361c0cd..f22feb3 100644
--- a/tests/TrustTests/Android.bp
+++ b/tests/TrustTests/Android.bp
@@ -40,3 +40,10 @@
platform_apis: true,
certificate: "platform",
}
+
+test_module_config {
+ name: "TrustTests_trust_test",
+ base: "TrustTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.trust.test"],
+}
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 887630b..b5cc553 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -59,7 +59,6 @@
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.test.TestLooper;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -73,10 +72,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.telephony.flags.Flags;
-
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -133,8 +129,6 @@
TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
}
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
@NonNull private final Context mContext;
@NonNull private final TestLooper mTestLooper;
@@ -193,7 +187,6 @@
@Before
public void setUp() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CRASH_ON_GETTING_CONFIG_WHEN_PHONE_IS_GONE);
doReturn(2).when(mTelephonyManager).getActiveModemCount();
mCallback = mock(TelephonySubscriptionTrackerCallback.class);
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
index 0439d5f5..edad678 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -123,7 +123,6 @@
mSetFlagsRule.enableFlags(Flags.FLAG_VALIDATE_NETWORK_ON_IPSEC_LOSS);
mSetFlagsRule.enableFlags(Flags.FLAG_EVALUATE_IPSEC_LOSS_ON_LP_NC_CHANGE);
mSetFlagsRule.enableFlags(Flags.FLAG_HANDLE_SEQ_NUM_LEAP);
- mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_DISABLE_IPSEC_LOSS_DETECTOR);
when(mNetwork.getNetId()).thenReturn(-1);
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 682adbc..ea77b8d 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -118,7 +118,6 @@
java_test_host {
name: "hoststubgentest",
- // main_class: "com.android.hoststubgen.Main",
srcs: ["test/**/*.kt"],
static_libs: [
"hoststubgen",
@@ -143,8 +142,7 @@
// "--policy-override-file $(location framework-policy-override.txt) " +
"@$(location :hoststubgen-standard-options) " +
- "--out-stub-jar $(location host_stub.jar) " +
- "--out-impl-jar $(location host_impl.jar) " +
+ "--out-jar $(location host.jar) " +
// "--keep-all-classes " + // Used it for an experiment. See KeepAllClassesFilter.
"--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
@@ -159,10 +157,8 @@
srcs: [
":hoststubgen-standard-options",
],
- // Create two jar files.
out: [
- "host_stub.jar",
- "host_impl.jar",
+ "host.jar",
// Following files are created just as FYI.
"hoststubgen_keep_all.txt",
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
similarity index 76%
copy from ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
copy to tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
index 4b9cf85..bc9471b 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotation;
+package android.hosttest.annotation;
-import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -24,13 +24,9 @@
/**
* THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
* QUESTIONS ABOUT IT.
- *
- * TODO: Javadoc
- *
* @hide
*/
-@Target({TYPE})
+@Target({METHOD})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodNativeSubstitutionClass {
- String value();
+public @interface HostSideTestRedirect {
}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
similarity index 95%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java
rename to tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
index 9c81383..28ad236 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
@@ -30,6 +30,6 @@
*/
@Target({TYPE})
@Retention(RetentionPolicy.CLASS)
-public @interface HostSideTestNativeSubstitutionClass {
+public @interface HostSideTestRedirectionClass {
String value();
}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java
deleted file mode 100644
index cabdfe0..0000000
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hosttest.annotation;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
- *
- * Mark a class, field or a method as "Stub", meaning tests can see the APIs.
- * When applied to a class, it will _not_ affect the visibility of its members. They need to be
- * individually marked.
- *
- * <p>In order to expose a class and all its members, use {@link HostSideTestWholeClassStub}
- * instead.
- *
- * @hide
- */
-@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
-@Retention(RetentionPolicy.CLASS)
-public @interface HostSideTestStub {
-}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java
deleted file mode 100644
index 1824f6f..0000000
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hosttest.annotation;
-
-import static java.lang.annotation.ElementType.TYPE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
- *
- * Same as {@link HostSideTestStub} but it'll change the visibility of all its members too.
- *
- * @hide
- */
-@Target({TYPE})
-@Retention(RetentionPolicy.CLASS)
-public @interface HostSideTestWholeClassStub {
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInStub.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInStub.java
deleted file mode 100644
index 12b9875..0000000
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInStub.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2023 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.hoststubgen.hosthelper;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation injected to all classes/methods/fields that are kept in the "stub" jar.
- *
- * All items in the stub jar are automatically kept in the impl jar as well, so
- * the items with this annotation will all have {@link HostStubGenKeptInImpl} too.
- */
-@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface HostStubGenKeptInStub {
- String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(HostStubGenKeptInStub.class);
- String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
index cb50404..b017103 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
@@ -23,8 +23,6 @@
/**
* Annotation injected to all methods processed as "ignore".
- *
- * (This annotation is only added in the impl jar, but not the stub jar)
*/
@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInImpl.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
similarity index 92%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInImpl.java
rename to tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
index 2cc500f..18ef1ba 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInImpl.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
@@ -25,11 +25,11 @@
import java.lang.annotation.Target;
/**
- * Annotation injected to all classes/methods/fields that are kept in the "impl" jar.
+ * Annotation injected to all classes/methods/fields that are kept in the processes jar.
*/
@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
@Retention(RetentionPolicy.RUNTIME)
-public @interface HostStubGenKeptInImpl {
- String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(HostStubGenKeptInImpl.class);
+public @interface HostStubGenProcessedAsKeep {
+ String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(HostStubGenProcessedAsKeep.class);
String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
index cfa4896..99e38c0 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
@@ -26,8 +26,6 @@
/**
* Annotation injected to all methods that are processed as "substitute".
- *
- * (This annotation is only added in the impl jar, but not the stub jar)
*/
@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
@Retention(RetentionPolicy.RUNTIME)
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
index 0d2da11..4933cf8 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
@@ -23,8 +23,6 @@
/**
* Annotation injected to all methods that are processed as "throw".
- *
- * (This annotation is only added in the impl jar, but not the stub jar)
*/
@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index 60eb47ee..78fd8f7 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -16,12 +16,8 @@
package com.android.hoststubgen.hosthelper;
import java.io.PrintStream;
-import java.lang.StackWalker.Option;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.util.HashMap;
-
-import javax.annotation.concurrent.GuardedBy;
/**
* Utilities used in the host side test environment.
@@ -101,68 +97,6 @@
+ methodName + methodDescriptor);
}
- private static final StackWalker sStackWalker =
- StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
-
- /**
- * Return a {@link StackWalker} that supports {@link StackWalker#getCallerClass()}.
- */
- public static StackWalker getStackWalker() {
- return sStackWalker;
- }
-
- /**
- * Cache used by {@link #isClassAllowedToCallNonStubMethods}.
- */
- @GuardedBy("sAllowedClasses")
- private static final HashMap<Class, Boolean> sAllowedClasses = new HashMap();
-
- /**
- * Return true if a given class is allowed to access non-stub methods -- that is, if the class
- * is in the hoststubgen generated JARs. (not in the test jar.)
- */
- private static boolean isClassAllowedToCallNonStubMethods(Class<?> clazz) {
- synchronized (sAllowedClasses) {
- var cached = sAllowedClasses.get(clazz);
- if (cached != null) {
- return cached;
- }
- }
- // All processed classes have this annotation.
- var allowed = clazz.getAnnotation(HostStubGenKeptInImpl.class) != null;
-
- // Java classes should be able to access any methods. (via callbacks, etc.)
- if (!allowed) {
- if (clazz.getPackageName().startsWith("java.")
- || clazz.getPackageName().startsWith("javax.")) {
- allowed = true;
- }
- }
- synchronized (sAllowedClasses) {
- sAllowedClasses.put(clazz, allowed);
- }
- return allowed;
- }
-
- /**
- * Called when non-stub methods are called. We do a host-unsupported method direct call check
- * in here.
- */
- public static void onNonStubMethodCalled(
- String methodClass,
- String methodName,
- String methodDescriptor,
- Class<?> callerClass) {
- if (SKIP_NON_STUB_METHOD_CHECK) {
- return;
- }
- if (isClassAllowedToCallNonStubMethods(callerClass)) {
- return; // Generated class is allowed to call framework class.
- }
- logPrintStream.println("! " + methodClass + "." + methodName + methodDescriptor
- + " called by " + callerClass.getCanonicalName());
- }
-
/**
* Called when any top level class (not nested classes) in the impl jar is loaded.
*
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
index c371b5d..eba8e62 100644
--- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
+++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
@@ -3,8 +3,6 @@
--debug
# Uncomment below lines to enable each feature.
---enable-non-stub-method-check
-# --no-non-stub-method-check
#--default-method-call-hook
# com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -13,15 +11,10 @@
# Standard annotations.
# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
---stub-annotation
- android.hosttest.annotation.HostSideTestStub
--keep-annotation
android.hosttest.annotation.HostSideTestKeep
---stub-class-annotation
- android.hosttest.annotation.HostSideTestWholeClassStub
-
--keep-class-annotation
android.hosttest.annotation.HostSideTestWholeClassKeep
@@ -34,8 +27,11 @@
--substitute-annotation
android.hosttest.annotation.HostSideTestSubstitute
---native-substitute-annotation
- android.hosttest.annotation.HostSideTestNativeSubstitutionClass
+--redirect-annotation
+ android.hosttest.annotation.HostSideTestRedirect
+
+--redirection-class-annotation
+ android.hosttest.annotation.HostSideTestRedirectionClass
--class-load-hook-annotation
android.hosttest.annotation.HostSideTestClassLoadHook
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index d97dd7c..084448d 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
@@ -43,9 +43,8 @@
cleanup_temp
-JAR=hoststubgen-test-tiny-framework.jar
-STUB=$TEMP/stub.jar
-IMPL=$TEMP/impl.jar
+INJAR=hoststubgen-test-tiny-framework.jar
+OUTJAR=$TEMP/host.jar
ANNOTATION_FILTER=$TEMP/annotation-filter.txt
@@ -81,27 +80,18 @@
cat $ANNOTATION_FILTER
fi
- local stub_arg=""
- local impl_arg=""
+ local out_arg=""
- if [[ "$STUB" != "" ]] ; then
- stub_arg="--out-stub-jar $STUB"
- fi
- if [[ "$IMPL" != "" ]] ; then
- impl_arg="--out-impl-jar $IMPL"
+ if [[ "$OUTJAR" != "" ]] ; then
+ out_arg="--out-jar $OUTJAR"
fi
hoststubgen \
--debug \
- --in-jar $JAR \
- $stub_arg \
- $impl_arg \
- --stub-annotation \
- android.hosttest.annotation.HostSideTestStub \
+ --in-jar $INJAR \
+ $out_arg \
--keep-annotation \
android.hosttest.annotation.HostSideTestKeep \
- --stub-class-annotation \
- android.hosttest.annotation.HostSideTestWholeClassStub \
--keep-class-annotation \
android.hosttest.annotation.HostSideTestWholeClassKeep \
--throw-annotation \
@@ -110,8 +100,10 @@
android.hosttest.annotation.HostSideTestRemove \
--substitute-annotation \
android.hosttest.annotation.HostSideTestSubstitute \
- --native-substitute-annotation \
- android.hosttest.annotation.HostSideTestNativeSubstitutionClass \
+ --redirect-annotation \
+ android.hosttest.annotation.HostSideTestRedirect \
+ --redirection-class-annotation \
+ android.hosttest.annotation.HostSideTestRedirectionClass \
--class-load-hook-annotation \
android.hosttest.annotation.HostSideTestClassLoadHook \
--keep-static-initializer-annotation \
@@ -213,9 +205,9 @@
"
run_hoststubgen_for_failure "One specific class disallowed" \
- "TinyFrameworkClassAnnotations is not allowed to have Ravenwood annotations" \
+ "TinyFrameworkAnnotations is not allowed to have Ravenwood annotations" \
"
-!com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+!com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
* # All other classes allowed
"
@@ -225,11 +217,7 @@
* # All other classes allowed
"
-STUB="" run_hoststubgen_for_success "No stub generation" ""
-
-IMPL="" run_hoststubgen_for_success "No impl generation" ""
-
-STUB="" IMPL="" run_hoststubgen_for_success "No stub, no impl generation" ""
+OUTJAR="" run_hoststubgen_for_success "No output generation" ""
EXTRA_ARGS="--in-jar abc" run_hoststubgen_for_failure "Duplicate arg" \
"Duplicate or conflicting argument found: --in-jar" \
@@ -237,4 +225,4 @@
echo "All tests passed"
-exit 0
\ No newline at end of file
+exit 0
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 7b08678..34aaaa9 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -24,19 +24,14 @@
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.FilterRemapper
import com.android.hoststubgen.filters.ImplicitOutputFilter
+import com.android.hoststubgen.filters.KeepNativeFilter
import com.android.hoststubgen.filters.OutputFilter
-import com.android.hoststubgen.filters.StubIntersectingFilter
+import com.android.hoststubgen.filters.SanitizationFilter
import com.android.hoststubgen.filters.createFilterFromTextPolicyFile
import com.android.hoststubgen.filters.printAsTextPolicy
import com.android.hoststubgen.utils.ClassFilter
import com.android.hoststubgen.visitors.BaseAdapter
import com.android.hoststubgen.visitors.PackageRedirectRemapper
-import org.objectweb.asm.ClassReader
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.ClassWriter
-import org.objectweb.asm.commons.ClassRemapper
-import org.objectweb.asm.commons.Remapper
-import org.objectweb.asm.util.CheckClassAdapter
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.FileOutputStream
@@ -46,6 +41,12 @@
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.ClassWriter
+import org.objectweb.asm.commons.ClassRemapper
+import org.objectweb.asm.commons.Remapper
+import org.objectweb.asm.util.CheckClassAdapter
/**
* Actual main class.
@@ -82,17 +83,16 @@
// Transform the jar.
convert(
- options.inJar.get,
- options.outStubJar.get,
- options.outImplJar.get,
- filter,
- options.enableClassChecker.get,
- allClasses,
- errors,
- stats,
- filterRemapper,
- options.numShards.get,
- options.shard.get,
+ options.inJar.get,
+ options.outJar.get,
+ filter,
+ options.enableClassChecker.get,
+ allClasses,
+ errors,
+ stats,
+ filterRemapper,
+ options.numShards.get,
+ options.shard.get,
)
// Dump statistics, if specified.
@@ -117,10 +117,10 @@
* jars, and "how". (e.g. with substitution?)
*/
private fun buildFilter(
- errors: HostStubGenErrors,
- allClasses: ClassNodes,
- options: HostStubGenOptions,
- ): OutputFilter {
+ errors: HostStubGenErrors,
+ allClasses: ClassNodes,
+ options: HostStubGenOptions,
+ ): OutputFilter {
// We build a "chain" of multiple filters here.
//
// The filters are build in from "inside", meaning the first filter created here is
@@ -134,6 +134,9 @@
// The first filter is for the default policy from the command line options.
var filter: OutputFilter = ConstantFilter(options.defaultPolicy.get, "default-by-options")
+ // Next, we build a filter that preserves all native methods by default
+ filter = KeepNativeFilter(allClasses, filter)
+
// Next, we need a filter that resolves "class-wide" policies.
// This is used when a member (methods, fields, nested classes) don't get any polices
// from upper filters. e.g. when a method has no annotations, then this filter will apply
@@ -159,18 +162,17 @@
filter = AnnotationBasedFilter(
errors,
allClasses,
- options.stubAnnotations,
options.keepAnnotations,
- options.stubClassAnnotations,
options.keepClassAnnotations,
options.throwAnnotations,
options.removeAnnotations,
options.substituteAnnotations,
- options.nativeSubstituteAnnotations,
+ options.redirectAnnotations,
+ options.redirectionClassAnnotations,
options.classLoadHookAnnotations,
options.keepStaticInitializerAnnotations,
annotationAllowedClassesFilter,
- filter,
+ filter
)
// Next, "text based" filter, which allows to override polices without touching
@@ -179,50 +181,31 @@
filter = createFilterFromTextPolicyFile(it, allClasses, filter)
}
- // If `--intersect-stub-jar` is provided, load from these jar files too.
- // We use this to restrict stub APIs to public/system/test APIs,
- // by intersecting with a stub jar file created by metalava.
- if (options.intersectStubJars.size > 0) {
- val intersectingJars = loadIntersectingJars(options.intersectStubJars)
-
- filter = StubIntersectingFilter(errors, intersectingJars, filter)
- }
-
// Apply the implicit filter.
filter = ImplicitOutputFilter(errors, allClasses, filter)
+ // Add a final sanitization step.
+ filter = SanitizationFilter(errors, allClasses, filter)
+
return filter
}
/**
- * Load jar files specified with "--intersect-stub-jar".
- */
- private fun loadIntersectingJars(filenames: Set<String>): Map<String, ClassNodes> {
- val intersectingJars = mutableMapOf<String, ClassNodes>()
-
- filenames.forEach { filename ->
- intersectingJars[filename] = ClassNodes.loadClassStructures(filename)
- }
- return intersectingJars
- }
-
- /**
* Convert a JAR file into "stub" and "impl" JAR files.
*/
private fun convert(
- inJar: String,
- outStubJar: String?,
- outImplJar: String?,
- filter: OutputFilter,
- enableChecker: Boolean,
- classes: ClassNodes,
- errors: HostStubGenErrors,
- stats: HostStubGenStats,
- remapper: Remapper?,
- numShards: Int,
- shard: Int,
- ) {
- log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar)
+ inJar: String,
+ outJar: String?,
+ filter: OutputFilter,
+ enableChecker: Boolean,
+ classes: ClassNodes,
+ errors: HostStubGenErrors,
+ stats: HostStubGenStats,
+ remapper: Remapper?,
+ numShards: Int,
+ shard: Int
+ ) {
+ log.i("Converting %s into %s ...", inJar, outJar)
log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
log.iTime("Transforming jar") {
@@ -240,29 +223,26 @@
val shardStart = numItems * shard / numShards
val shardNextStart = numItems * (shard + 1) / numShards
- maybeWithZipOutputStream(outStubJar) { stubOutStream ->
- maybeWithZipOutputStream(outImplJar) { implOutStream ->
- val inEntries = inZip.entries()
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
- val inShard = (shardStart <= itemIndex)
- && (itemIndex < shardNextStart)
- itemIndex++
- if (!inShard) {
- continue
- }
- convertSingleEntry(
- inZip, entry, stubOutStream, implOutStream,
- filter, packageRedirector, remapper,
- enableChecker, classes, errors, stats
- )
- numItemsProcessed++
+ maybeWithZipOutputStream(outJar) { outStream ->
+ val inEntries = inZip.entries()
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
+ val inShard = (shardStart <= itemIndex)
+ && (itemIndex < shardNextStart)
+ itemIndex++
+ if (!inShard) {
+ continue
}
- log.i("Converted all entries.")
+ convertSingleEntry(
+ inZip, entry, outStream, filter,
+ packageRedirector, remapper, enableChecker,
+ classes, errors, stats
+ )
+ numItemsProcessed++
}
+ log.i("Converted all entries.")
}
- outStubJar?.let { log.i("Created stub: $it") }
- outImplJar?.let { log.i("Created impl: $it") }
+ outJar?.let { log.i("Created: $it") }
}
}
log.i("%d / %d item(s) processed.", numItemsProcessed, numItems)
@@ -280,18 +260,17 @@
* Convert a single ZIP entry, which may or may not be a class file.
*/
private fun convertSingleEntry(
- inZip: ZipFile,
- entry: ZipEntry,
- stubOutStream: ZipOutputStream?,
- implOutStream: ZipOutputStream?,
- filter: OutputFilter,
- packageRedirector: PackageRedirectRemapper,
- remapper: Remapper?,
- enableChecker: Boolean,
- classes: ClassNodes,
- errors: HostStubGenErrors,
- stats: HostStubGenStats,
- ) {
+ inZip: ZipFile,
+ entry: ZipEntry,
+ outStream: ZipOutputStream?,
+ filter: OutputFilter,
+ packageRedirector: PackageRedirectRemapper,
+ remapper: Remapper?,
+ enableChecker: Boolean,
+ classes: ClassNodes,
+ errors: HostStubGenErrors,
+ stats: HostStubGenStats
+ ) {
log.d("Entry: %s", entry.name)
log.withIndent {
val name = entry.name
@@ -303,8 +282,10 @@
// If it's a class, convert it.
if (name.endsWith(".class")) {
- processSingleClass(inZip, entry, stubOutStream, implOutStream, filter,
- packageRedirector, remapper, enableChecker, classes, errors, stats)
+ processSingleClass(
+ inZip, entry, outStream, filter, packageRedirector,
+ remapper, enableChecker, classes, errors, stats
+ )
return
}
@@ -312,17 +293,14 @@
// - *.uau seems to contain hidden API information.
// - *_compat_config.xml is also about compat-framework.
- if (name.endsWith(".uau") ||
- name.endsWith("_compat_config.xml")) {
+ if (name.endsWith(".uau") || name.endsWith("_compat_config.xml")) {
log.d("Not needed: %s", entry.name)
return
}
// Unknown type, we just copy it to both output zip files.
- // TODO: We probably shouldn't do it for stub jar?
log.v("Copying: %s", entry.name)
- stubOutStream?.let { copyZipEntry(inZip, entry, it) }
- implOutStream?.let { copyZipEntry(inZip, entry, it) }
+ outStream?.let { copyZipEntry(inZip, entry, it) }
}
}
@@ -330,10 +308,10 @@
* Copy a single ZIP entry to the output.
*/
private fun copyZipEntry(
- inZip: ZipFile,
- entry: ZipEntry,
- out: ZipOutputStream,
- ) {
+ inZip: ZipFile,
+ entry: ZipEntry,
+ out: ZipOutputStream,
+ ) {
// TODO: It seems like copying entries this way is _very_ slow,
// even with out.setLevel(0). Look for other ways to do it.
@@ -350,18 +328,17 @@
* Convert a single class to "stub" and "impl".
*/
private fun processSingleClass(
- inZip: ZipFile,
- entry: ZipEntry,
- stubOutStream: ZipOutputStream?,
- implOutStream: ZipOutputStream?,
- filter: OutputFilter,
- packageRedirector: PackageRedirectRemapper,
- remapper: Remapper?,
- enableChecker: Boolean,
- classes: ClassNodes,
- errors: HostStubGenErrors,
- stats: HostStubGenStats,
- ) {
+ inZip: ZipFile,
+ entry: ZipEntry,
+ outStream: ZipOutputStream?,
+ filter: OutputFilter,
+ packageRedirector: PackageRedirectRemapper,
+ remapper: Remapper?,
+ enableChecker: Boolean,
+ classes: ClassNodes,
+ errors: HostStubGenErrors,
+ stats: HostStubGenStats
+ ) {
val classInternalName = entry.name.replaceFirst("\\.class$".toRegex(), "")
val classPolicy = filter.getPolicyForClass(classInternalName)
if (classPolicy.policy == FilterPolicy.Remove) {
@@ -373,33 +350,22 @@
remapper?.mapType(classInternalName)?.let { remappedName ->
if (remappedName != classInternalName) {
log.d("Renaming class file: %s -> %s", classInternalName, remappedName)
- newName = remappedName + ".class"
+ newName = "$remappedName.class"
}
}
- // Generate stub first.
- if (stubOutStream != null && classPolicy.policy.needsInStub) {
- log.v("Creating stub class: %s Policy: %s", classInternalName, classPolicy)
+
+ if (outStream != null) {
+ log.v("Creating class: %s Policy: %s", classInternalName, classPolicy)
log.withIndent {
BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
val newEntry = ZipEntry(newName)
- stubOutStream.putNextEntry(newEntry)
- convertClass(classInternalName, /*forImpl=*/false, bis,
- stubOutStream, filter, packageRedirector, remapper,
- enableChecker, classes, errors, null)
- stubOutStream.closeEntry()
- }
- }
- }
- if (implOutStream != null && classPolicy.policy.needsInImpl) {
- log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy)
- log.withIndent {
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- val newEntry = ZipEntry(newName)
- implOutStream.putNextEntry(newEntry)
- convertClass(classInternalName, /*forImpl=*/true, bis,
- implOutStream, filter, packageRedirector, remapper,
- enableChecker, classes, errors, stats)
- implOutStream.closeEntry()
+ outStream.putNextEntry(newEntry)
+ convertClass(
+ classInternalName, bis,
+ outStream, filter, packageRedirector, remapper,
+ enableChecker, classes, errors, stats
+ )
+ outStream.closeEntry()
}
}
}
@@ -409,18 +375,17 @@
* Convert a single class to either "stub" or "impl".
*/
private fun convertClass(
- classInternalName: String,
- forImpl: Boolean,
- input: InputStream,
- out: OutputStream,
- filter: OutputFilter,
- packageRedirector: PackageRedirectRemapper,
- remapper: Remapper?,
- enableChecker: Boolean,
- classes: ClassNodes,
- errors: HostStubGenErrors,
- stats: HostStubGenStats?,
- ) {
+ classInternalName: String,
+ input: InputStream,
+ out: OutputStream,
+ filter: OutputFilter,
+ packageRedirector: PackageRedirectRemapper,
+ remapper: Remapper?,
+ enableChecker: Boolean,
+ classes: ClassNodes,
+ errors: HostStubGenErrors,
+ stats: HostStubGenStats?
+ ) {
val cr = ClassReader(input)
// COMPUTE_FRAMES wouldn't be happy if code uses
@@ -439,14 +404,15 @@
}
val visitorOptions = BaseAdapter.Options(
- enablePreTrace = options.enablePreTrace.get,
- enablePostTrace = options.enablePostTrace.get,
- enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection.get,
- errors = errors,
- stats = stats,
+ errors = errors,
+ stats = stats,
+ enablePreTrace = options.enablePreTrace.get,
+ enablePostTrace = options.enablePostTrace.get,
)
- outVisitor = BaseAdapter.getVisitor(classInternalName, classes, outVisitor, filter,
- packageRedirector, remapper, forImpl, visitorOptions)
+ outVisitor = BaseAdapter.getVisitor(
+ classInternalName, classes, outVisitor, filter,
+ packageRedirector, visitorOptions
+ )
cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
val data = cw.toByteArray()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index f88b107..057a52c 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -74,25 +74,21 @@
/** Input jar file*/
var inJar: SetOnce<String> = SetOnce(""),
- /** Output stub jar file */
- var outStubJar: SetOnce<String?> = SetOnce(null),
-
- /** Output implementation jar file */
- var outImplJar: SetOnce<String?> = SetOnce(null),
+ /** Output jar file */
+ var outJar: SetOnce<String?> = SetOnce(null),
var inputJarDumpFile: SetOnce<String?> = SetOnce(null),
var inputJarAsKeepAllFile: SetOnce<String?> = SetOnce(null),
- var stubAnnotations: MutableSet<String> = mutableSetOf(),
var keepAnnotations: MutableSet<String> = mutableSetOf(),
var throwAnnotations: MutableSet<String> = mutableSetOf(),
var removeAnnotations: MutableSet<String> = mutableSetOf(),
- var stubClassAnnotations: MutableSet<String> = mutableSetOf(),
var keepClassAnnotations: MutableSet<String> = mutableSetOf(),
+ var redirectAnnotations: MutableSet<String> = mutableSetOf(),
var substituteAnnotations: MutableSet<String> = mutableSetOf(),
- var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(),
+ var redirectionClassAnnotations: MutableSet<String> = mutableSetOf(),
var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
var keepStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),
@@ -103,8 +99,6 @@
var defaultClassLoadHook: SetOnce<String?> = SetOnce(null),
var defaultMethodCallHook: SetOnce<String?> = SetOnce(null),
- var intersectStubJars: MutableSet<String> = mutableSetOf(),
-
var policyOverrideFile: SetOnce<String?> = SetOnce(null),
var defaultPolicy: SetOnce<FilterPolicy> = SetOnce(FilterPolicy.Remove),
@@ -115,8 +109,6 @@
var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
var enablePostTrace: SetOnce<Boolean> = SetOnce(false),
- var enableNonStubMethodCallDetection: SetOnce<Boolean> = SetOnce(false),
-
var statsFile: SetOnce<String?> = SetOnce(null),
var apiListFile: SetOnce<String?> = SetOnce(null),
@@ -150,10 +142,7 @@
}
while (true) {
- val arg = ai.nextArgOptional()
- if (arg == null) {
- break
- }
+ val arg = ai.nextArgOptional() ?: break
// Define some shorthands...
fun nextArg(): String = ai.nextArgRequired(arg)
@@ -169,8 +158,9 @@
"-h", "--help" -> TODO("Help is not implemented yet")
"--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists()
- "--out-stub-jar" -> ret.outStubJar.set(nextArg())
- "--out-impl-jar" -> ret.outImplJar.set(nextArg())
+ // We support both arguments because some AOSP dependencies
+ // still use the old argument
+ "--out-jar", "--out-impl-jar" -> ret.outJar.set(nextArg())
"--policy-override-file" ->
ret.policyOverrideFile.set(nextArg())!!.ensureFileExists()
@@ -181,17 +171,10 @@
"--default-remove" -> ret.defaultPolicy.set(FilterPolicy.Remove)
"--default-throw" -> ret.defaultPolicy.set(FilterPolicy.Throw)
"--default-keep" -> ret.defaultPolicy.set(FilterPolicy.Keep)
- "--default-stub" -> ret.defaultPolicy.set(FilterPolicy.Stub)
-
- "--stub-annotation" ->
- ret.stubAnnotations.addUniqueAnnotationArg()
"--keep-annotation" ->
ret.keepAnnotations.addUniqueAnnotationArg()
- "--stub-class-annotation" ->
- ret.stubClassAnnotations.addUniqueAnnotationArg()
-
"--keep-class-annotation" ->
ret.keepClassAnnotations.addUniqueAnnotationArg()
@@ -204,8 +187,11 @@
"--substitute-annotation" ->
ret.substituteAnnotations.addUniqueAnnotationArg()
- "--native-substitute-annotation" ->
- ret.nativeSubstituteAnnotations.addUniqueAnnotationArg()
+ "--redirect-annotation" ->
+ ret.redirectAnnotations.addUniqueAnnotationArg()
+
+ "--redirection-class-annotation" ->
+ ret.redirectionClassAnnotations.addUniqueAnnotationArg()
"--class-load-hook-annotation" ->
ret.classLoadHookAnnotations.addUniqueAnnotationArg()
@@ -225,9 +211,6 @@
"--default-method-call-hook" ->
ret.defaultMethodCallHook.set(nextArg())
- "--intersect-stub-jar" ->
- ret.intersectStubJars += nextArg().ensureFileExists()
-
"--gen-keep-all-file" ->
ret.inputJarAsKeepAllFile.set(nextArg())
@@ -241,12 +224,6 @@
"--enable-post-trace" -> ret.enablePostTrace.set(true)
"--no-post-trace" -> ret.enablePostTrace.set(false)
- "--enable-non-stub-method-check" ->
- ret.enableNonStubMethodCallDetection.set(true)
-
- "--no-non-stub-method-check" ->
- ret.enableNonStubMethodCallDetection.set(false)
-
"--gen-input-dump-file" -> ret.inputJarDumpFile.set(nextArg())
"--stats-file" -> ret.statsFile.set(nextArg())
@@ -273,9 +250,8 @@
if (!ret.inJar.isSet) {
throw ArgumentsException("Required option missing: --in-jar")
}
- if (!ret.outStubJar.isSet && !ret.outImplJar.isSet) {
- log.w("Neither --out-stub-jar nor --out-impl-jar is set." +
- " $executableName will not generate jar files.")
+ if (!ret.outJar.isSet) {
+ log.w("--out-jar is not set. $executableName will not generate jar files.")
}
if (ret.numShards.isSet != ret.shard.isSet) {
throw ArgumentsException("--num-shards and --shard-index must be used together")
@@ -287,11 +263,6 @@
}
}
- if (ret.enableNonStubMethodCallDetection.get) {
- log.w("--enable-non-stub-method-check is not fully implemented yet." +
- " See the todo in doesMethodNeedNonStubCallCheck().")
- }
-
return ret
}
}
@@ -300,32 +271,27 @@
return """
HostStubGenOptions{
inJar='$inJar',
- outStubJar='$outStubJar',
- outImplJar='$outImplJar',
+ outJar='$outJar',
inputJarDumpFile=$inputJarDumpFile,
inputJarAsKeepAllFile=$inputJarAsKeepAllFile,
- stubAnnotations=$stubAnnotations,
keepAnnotations=$keepAnnotations,
throwAnnotations=$throwAnnotations,
removeAnnotations=$removeAnnotations,
- stubClassAnnotations=$stubClassAnnotations,
keepClassAnnotations=$keepClassAnnotations,
substituteAnnotations=$substituteAnnotations,
- nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
+ nativeSubstituteAnnotations=$redirectionClassAnnotations,
classLoadHookAnnotations=$classLoadHookAnnotations,
keepStaticInitializerAnnotations=$keepStaticInitializerAnnotations,
packageRedirects=$packageRedirects,
- $annotationAllowedClassesFile=$annotationAllowedClassesFile,
+ annotationAllowedClassesFile=$annotationAllowedClassesFile,
defaultClassLoadHook=$defaultClassLoadHook,
defaultMethodCallHook=$defaultMethodCallHook,
- intersectStubJars=$intersectStubJars,
policyOverrideFile=$policyOverrideFile,
defaultPolicy=$defaultPolicy,
cleanUpOnError=$cleanUpOnError,
enableClassChecker=$enableClassChecker,
enablePreTrace=$enablePreTrace,
enablePostTrace=$enablePostTrace,
- enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection,
statsFile=$statsFile,
apiListFile=$apiListFile,
numShards=$numShards,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 6cf2143..a02082d 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -29,33 +29,24 @@
/** Name of the class initializer method. */
-val CLASS_INITIALIZER_NAME = "<clinit>"
+const val CLASS_INITIALIZER_NAME = "<clinit>"
/** Descriptor of the class initializer method. */
-val CLASS_INITIALIZER_DESC = "()V"
+const val CLASS_INITIALIZER_DESC = "()V"
/** Name of constructors. */
-val CTOR_NAME = "<init>"
+const val CTOR_NAME = "<init>"
/**
- * Find any of [anyAnnotations] from the list of visible / invisible annotations.
+ * Find any of [set] from the list of visible / invisible annotations.
*/
fun findAnyAnnotation(
- anyAnnotations: Set<String>,
- visibleAnnotations: List<AnnotationNode>?,
- invisibleAnnotations: List<AnnotationNode>?,
- ): AnnotationNode? {
- for (an in visibleAnnotations ?: emptyList()) {
- if (anyAnnotations.contains(an.desc)) {
- return an
- }
- }
- for (an in invisibleAnnotations ?: emptyList()) {
- if (anyAnnotations.contains(an.desc)) {
- return an
- }
- }
- return null
+ set: Set<String>,
+ visibleAnnotations: List<AnnotationNode>?,
+ invisibleAnnotations: List<AnnotationNode>?,
+): AnnotationNode? {
+ return visibleAnnotations?.find { it.desc in set }
+ ?: invisibleAnnotations?.find { it.desc in set }
}
fun ClassNode.findAnyAnnotation(set: Set<String>): AnnotationNode? {
@@ -70,6 +61,27 @@
return findAnyAnnotation(set, this.visibleAnnotations, this.invisibleAnnotations)
}
+fun findAllAnnotations(
+ set: Set<String>,
+ visibleAnnotations: List<AnnotationNode>?,
+ invisibleAnnotations: List<AnnotationNode>?
+): List<AnnotationNode> {
+ return (visibleAnnotations ?: emptyList()).filter { it.desc in set } +
+ (invisibleAnnotations ?: emptyList()).filter { it.desc in set }
+}
+
+fun ClassNode.findAllAnnotations(set: Set<String>): List<AnnotationNode> {
+ return findAllAnnotations(set, this.visibleAnnotations, this.invisibleAnnotations)
+}
+
+fun MethodNode.findAllAnnotations(set: Set<String>): List<AnnotationNode> {
+ return findAllAnnotations(set, this.visibleAnnotations, this.invisibleAnnotations)
+}
+
+fun FieldNode.findAllAnnotations(set: Set<String>): List<AnnotationNode> {
+ return findAllAnnotations(set, this.visibleAnnotations, this.invisibleAnnotations)
+}
+
fun <T> findAnnotationValueAsObject(
an: AnnotationNode,
propertyName: String,
@@ -202,18 +214,6 @@
}
/**
- * Take a class name. If it's a nested class, then return the name of its direct outer class name.
- * Otherwise, return null.
- */
-fun getDirectOuterClassName(className: String): String? {
- val pos = className.lastIndexOf('$')
- if (pos < 0) {
- return null
- }
- return className.substring(0, pos)
-}
-
-/**
* Write bytecode to push all the method arguments to the stack.
* The number of arguments and their type are taken from [methodDescriptor].
*/
@@ -339,6 +339,10 @@
return (this.access and Opcodes.ACC_PUBLIC) != 0
}
+fun MethodNode.isNative(): Boolean {
+ return (this.access and Opcodes.ACC_NATIVE) != 0
+}
+
fun MethodNode.isSpecial(): Boolean {
return CTOR_NAME == this.name || CLASS_INITIALIZER_NAME == this.name
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
index aaefee4..5e4e70f 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
@@ -44,7 +44,7 @@
val descriptor: String,
)
- val javaStandardApiPolicy = FilterPolicy.Stub.withReason("Java standard API")
+ private val javaStandardApiPolicy = FilterPolicy.Keep.withReason("Java standard API")
private val shownMethods = mutableSetOf<MethodKey>()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index 248121c..a6b8cdb 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -17,14 +17,16 @@
import com.android.hoststubgen.ClassParseException
import com.android.hoststubgen.HostStubGenErrors
-import com.android.hoststubgen.HostStubGenInternalException
import com.android.hoststubgen.InvalidAnnotationException
-import com.android.hoststubgen.addNonNullElement
+import com.android.hoststubgen.addLists
import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.findAllAnnotations
import com.android.hoststubgen.asm.findAnnotationValueAsString
import com.android.hoststubgen.asm.findAnyAnnotation
+import com.android.hoststubgen.asm.getPackageNameFromFullClassName
+import com.android.hoststubgen.asm.resolveClassNameWithDefaultPackage
import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.asm.toHumanReadableMethodName
import com.android.hoststubgen.asm.toJvmClassName
@@ -35,395 +37,309 @@
// TODO: Detect invalid cases, such as...
// - Class's visibility is lower than the members'.
-// - HostSideTestSubstituteWith is set, but it doesn't have @Stub or @Keep
/**
* [OutputFilter] using Java annotations.
*/
class AnnotationBasedFilter(
- private val errors: HostStubGenErrors,
- private val classes: ClassNodes,
- stubAnnotations_: Set<String>,
- keepAnnotations_: Set<String>,
- stubClassAnnotations_: Set<String>,
- keepClassAnnotations_: Set<String>,
- throwAnnotations_: Set<String>,
- removeAnnotations_: Set<String>,
- substituteAnnotations_: Set<String>,
- nativeSubstituteAnnotations_: Set<String>,
- classLoadHookAnnotations_: Set<String>,
- keepStaticInitializerAnnotations_: Set<String>,
- private val annotationAllowedClassesFilter: ClassFilter,
- fallback: OutputFilter,
+ private val errors: HostStubGenErrors,
+ private val classes: ClassNodes,
+ keepAnnotations_: Set<String>,
+ keepClassAnnotations_: Set<String>,
+ throwAnnotations_: Set<String>,
+ removeAnnotations_: Set<String>,
+ substituteAnnotations_: Set<String>,
+ redirectAnnotations_: Set<String>,
+ redirectionClassAnnotations_: Set<String>,
+ classLoadHookAnnotations_: Set<String>,
+ keepStaticInitializerAnnotations_: Set<String>,
+ private val annotationAllowedClassesFilter: ClassFilter,
+ fallback: OutputFilter,
) : DelegatingFilter(fallback) {
- private var stubAnnotations = convertToInternalNames(stubAnnotations_)
- private var keepAnnotations = convertToInternalNames(keepAnnotations_)
- private var stubClassAnnotations = convertToInternalNames(stubClassAnnotations_)
- private var keepClassAnnotations = convertToInternalNames(keepClassAnnotations_)
- private var throwAnnotations = convertToInternalNames(throwAnnotations_)
- private var removeAnnotations = convertToInternalNames(removeAnnotations_)
- private var substituteAnnotations = convertToInternalNames(substituteAnnotations_)
- private var nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_)
- private var classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_)
- private var keepStaticInitializerAnnotations =
- convertToInternalNames(keepStaticInitializerAnnotations_)
+ private val keepAnnotations = convertToInternalNames(keepAnnotations_)
+ private val keepClassAnnotations = convertToInternalNames(keepClassAnnotations_)
+ private val throwAnnotations = convertToInternalNames(throwAnnotations_)
+ private val removeAnnotations = convertToInternalNames(removeAnnotations_)
+ private val redirectAnnotations = convertToInternalNames(redirectAnnotations_)
+ private val substituteAnnotations = convertToInternalNames(substituteAnnotations_)
+ private val redirectionClassAnnotations =
+ convertToInternalNames(redirectionClassAnnotations_)
+ private val classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_)
+ private val keepStaticInitializerAnnotations =
+ convertToInternalNames(keepStaticInitializerAnnotations_)
/** Annotations that control API visibility. */
- private var visibilityAnnotations: Set<String> = convertToInternalNames(
- stubAnnotations_ +
- keepAnnotations_ +
- stubClassAnnotations_ +
- keepClassAnnotations_ +
- throwAnnotations_ +
- removeAnnotations_)
+ private val visibilityAnnotations = keepAnnotations +
+ keepClassAnnotations +
+ throwAnnotations +
+ removeAnnotations +
+ redirectAnnotations +
+ substituteAnnotations
+
+ /** All the annotations we use. */
+ private val allAnnotations = visibilityAnnotations +
+ redirectionClassAnnotations +
+ classLoadHookAnnotations +
+ keepStaticInitializerAnnotations
/**
* All the annotations we use. Note, this one is in a [convertToJvmNames] format unlike
* other ones, because of how it's used.
*/
- private var allAnnotations: Set<String> = convertToJvmNames(
- stubAnnotations_ +
- keepAnnotations_ +
- stubClassAnnotations_ +
+ private val allAnnotationClasses: Set<String> = convertToJvmNames(
+ keepAnnotations_ +
keepClassAnnotations_ +
throwAnnotations_ +
removeAnnotations_ +
+ redirectAnnotations_ +
substituteAnnotations_ +
- nativeSubstituteAnnotations_ +
- classLoadHookAnnotations_)
+ redirectionClassAnnotations_ +
+ classLoadHookAnnotations_ +
+ keepStaticInitializerAnnotations_
+ )
- private val substitutionHelper = SubstitutionHelper()
+ private val policyCache = mutableMapOf<String, ClassAnnotations>()
- private val reasonAnnotation = "annotation"
- private val reasonClassAnnotation = "class-annotation"
-
- /**
- * Throw if an item has more than one visibility annotations.
- *
- * name1 - 4 are only used in exception messages. We take them as separate strings
- * to avoid unnecessary string concatenations.
- */
- private fun detectInvalidAnnotations(
- visibles: List<AnnotationNode>?,
- invisibles: List<AnnotationNode>?,
- type: String,
- name1: String,
- name2: String,
- name3: String,
- ) {
- var count = 0
- for (an in visibles ?: emptyList()) {
- if (visibilityAnnotations.contains(an.desc)) {
- count++
- }
- }
- for (an in invisibles ?: emptyList()) {
- if (visibilityAnnotations.contains(an.desc)) {
- count++
- }
- }
- if (count > 1) {
- val description = if (name2 == "" && name3 == "") {
- "$type $name1"
- } else {
- "$type $name1.$name2$name3"
- }
- throw InvalidAnnotationException(
- "Found more than one visibility annotations on $description")
+ private val AnnotationNode.policy: FilterPolicyWithReason? get() {
+ return when (desc) {
+ in keepAnnotations -> FilterPolicy.Keep.withReason(REASON_ANNOTATION)
+ in keepClassAnnotations -> FilterPolicy.KeepClass.withReason(REASON_CLASS_ANNOTATION)
+ in substituteAnnotations -> FilterPolicy.Substitute.withReason(REASON_ANNOTATION)
+ in throwAnnotations -> FilterPolicy.Throw.withReason(REASON_ANNOTATION)
+ in removeAnnotations -> FilterPolicy.Remove.withReason(REASON_ANNOTATION)
+ in redirectAnnotations -> FilterPolicy.Redirect.withReason(REASON_ANNOTATION)
+ else -> null
}
}
- fun findAnyAnnotation(
- className: String,
- anyAnnotations: Set<String>,
- visibleAnnotations: List<AnnotationNode>?,
- invisibleAnnotations: List<AnnotationNode>?,
- ): AnnotationNode? {
- val ret = findAnyAnnotation(anyAnnotations, visibleAnnotations, invisibleAnnotations)
+ private fun getAnnotationPolicy(cn: ClassNode): ClassAnnotations {
+ return policyCache.getOrPut(cn.name) { ClassAnnotations(cn) }
+ }
- if (ret != null) {
- if (!annotationAllowedClassesFilter.matches(className)) {
- throw InvalidAnnotationException(
- "Class ${className.toHumanReadableClassName()} is not allowed to have " +
- "Ravenwood annotations. Contact g/ravenwood for more details.")
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ // If it's any of the annotations, then always keep it.
+ if (allAnnotationClasses.contains(className)) {
+ return FilterPolicy.KeepClass.withReason("HostStubGen Annotation")
+ }
+
+ val cn = classes.getClass(className)
+ return getAnnotationPolicy(cn).classPolicy ?: super.getPolicyForClass(className)
+ }
+
+ override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
+ val cn = classes.getClass(className)
+ return getAnnotationPolicy(cn).fieldPolicies[fieldName]
+ ?: super.getPolicyForField(className, fieldName)
+ }
+
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): FilterPolicyWithReason {
+ val cn = classes.getClass(className)
+ return getAnnotationPolicy(cn).methodPolicies[MethodKey(methodName, descriptor)]
+ ?: super.getPolicyForMethod(className, methodName, descriptor)
+ }
+
+ override fun getRenameTo(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): String? {
+ val cn = classes.getClass(className)
+ return getAnnotationPolicy(cn).renamedMethods[MethodKey(methodName, descriptor)]
+ ?: super.getRenameTo(className, methodName, descriptor)
+ }
+
+ override fun getRedirectionClass(className: String): String? {
+ val cn = classes.getClass(className)
+ return getAnnotationPolicy(cn).redirectionClass
+ }
+
+ override fun getClassLoadHooks(className: String): List<String> {
+ val cn = classes.getClass(className)
+ return addLists(super.getClassLoadHooks(className), getAnnotationPolicy(cn).classLoadHooks)
+ }
+
+ private data class MethodKey(val name: String, val desc: String)
+
+ /**
+ * Every time we see a class, we scan all its methods for substitution attributes,
+ * and compute (implicit) policies caused by them.
+ *
+ * For example, for the following methods:
+ *
+ * @Substitute(suffix = "_host")
+ * private void foo() {
+ * // This isn't supported on the host side.
+ * }
+ * private void foo_host() {
+ * // Host side implementation
+ * }
+ *
+ * We internally handle them as:
+ *
+ * foo() -> Substitute
+ * foo_host() -> Stub, and then rename it to foo().
+ */
+ private inner class ClassAnnotations(cn: ClassNode) {
+
+ val classPolicy: FilterPolicyWithReason?
+ val fieldPolicies = mutableMapOf<String, FilterPolicyWithReason>()
+ val methodPolicies = mutableMapOf<MethodKey, FilterPolicyWithReason>()
+ val renamedMethods = mutableMapOf<MethodKey, String>()
+ val redirectionClass: String?
+ val classLoadHooks: List<String>
+
+ init {
+ val allowAnnotation = annotationAllowedClassesFilter.matches(cn.name)
+ detectInvalidAnnotations(
+ cn.name, allowAnnotation,
+ cn.visibleAnnotations, cn.invisibleAnnotations,
+ "class", cn.name
+ )
+ classPolicy = cn.findAnyAnnotation(visibilityAnnotations)?.policy
+ redirectionClass = cn.findAnyAnnotation(redirectionClassAnnotations)?.let { an ->
+ getAnnotationField(an, "value")?.let { resolveRelativeClass(cn, it) }
+ }
+ classLoadHooks = cn.findAllAnnotations(classLoadHookAnnotations).mapNotNull { an ->
+ getAnnotationField(an, "value")?.toHumanReadableMethodName()
+ }
+ if (cn.findAnyAnnotation(keepStaticInitializerAnnotations) != null) {
+ methodPolicies[MethodKey(CLASS_INITIALIZER_NAME, CLASS_INITIALIZER_DESC)] =
+ FilterPolicy.Keep.withReason(REASON_ANNOTATION)
+ }
+
+ for (fn in cn.fields ?: emptyList()) {
+ detectInvalidAnnotations(
+ cn.name, allowAnnotation,
+ fn.visibleAnnotations, fn.invisibleAnnotations,
+ "field", cn.name, fn.name
+ )
+ fn.findAnyAnnotation(visibilityAnnotations)?.policy?.let {
+ fieldPolicies[fn.name] = it
+ }
+ }
+
+ for (mn in cn.methods ?: emptyList()) {
+ detectInvalidAnnotations(
+ cn.name, allowAnnotation,
+ mn.visibleAnnotations, mn.invisibleAnnotations,
+ "method", cn.name, mn.name, mn.desc
+ )
+
+ val an = mn.findAnyAnnotation(visibilityAnnotations) ?: continue
+ val policy = an.policy ?: continue
+ methodPolicies[MethodKey(mn.name, mn.desc)] = policy
+
+ if (policy.policy != FilterPolicy.Substitute) continue
+
+ // Handle substitution
+ val suffix = getAnnotationField(an, "suffix", false) ?: "\$ravenwood"
+ val replacement = mn.name + suffix
+
+ if (replacement == mn.name) {
+ errors.onErrorFound("@SubstituteWith require a different name")
+ } else {
+ // The replacement method has to be renamed
+ methodPolicies[MethodKey(replacement, mn.desc)] =
+ FilterPolicy.Keep.withReason(REASON_ANNOTATION)
+ renamedMethods[MethodKey(replacement, mn.desc)] = mn.name
+
+ log.v("Substitution found: %s%s -> %s", replacement, mn.desc, mn.name)
+ }
}
}
- return ret
- }
-
- /**
- * Find a visibility annotation.
- *
- * name1 - 4 are only used in exception messages.
- */
- private fun findAnnotation(
+ /**
+ * Throw if an item has more than one visibility annotations, or the class is not allowed
+ *
+ * name1 - 4 are only used in exception messages. We take them as separate strings
+ * to avoid unnecessary string concatenations.
+ */
+ private fun detectInvalidAnnotations(
className: String,
+ allowAnnotation: Boolean,
visibles: List<AnnotationNode>?,
invisibles: List<AnnotationNode>?,
type: String,
name1: String,
name2: String = "",
name3: String = "",
- ): FilterPolicyWithReason? {
- detectInvalidAnnotations(visibles, invisibles, type, name1, name2, name3)
-
- findAnyAnnotation(className, stubAnnotations, visibles, invisibles)?.let {
- return FilterPolicy.Stub.withReason(reasonAnnotation)
- }
- findAnyAnnotation(className, stubClassAnnotations, visibles, invisibles)?.let {
- return FilterPolicy.StubClass.withReason(reasonClassAnnotation)
- }
- findAnyAnnotation(className, keepAnnotations, visibles, invisibles)?.let {
- return FilterPolicy.Keep.withReason(reasonAnnotation)
- }
- findAnyAnnotation(className, keepClassAnnotations, visibles, invisibles)?.let {
- return FilterPolicy.KeepClass.withReason(reasonClassAnnotation)
- }
- findAnyAnnotation(className, throwAnnotations, visibles, invisibles)?.let {
- return FilterPolicy.Throw.withReason(reasonAnnotation)
- }
- findAnyAnnotation(className, removeAnnotations, visibles, invisibles)?.let {
- return FilterPolicy.Remove.withReason(reasonAnnotation)
- }
-
- return null
- }
-
- override fun getPolicyForClass(className: String): FilterPolicyWithReason {
- val cn = classes.getClass(className)
-
- findAnnotation(
- cn.name,
- cn.visibleAnnotations,
- cn.invisibleAnnotations,
- "class",
- className)?.let {
- return it
- }
-
- // If it's any of the annotations, then always keep it.
- if (allAnnotations.contains(className)) {
- return FilterPolicy.KeepClass.withReason("HostStubGen Annotation")
- }
-
- return super.getPolicyForClass(className)
- }
-
- override fun getPolicyForField(
- className: String,
- fieldName: String
- ): FilterPolicyWithReason {
- val cn = classes.getClass(className)
-
- cn.fields?.firstOrNull { it.name == fieldName }?.let {fn ->
- findAnnotation(
- cn.name,
- fn.visibleAnnotations,
- fn.invisibleAnnotations,
- "field",
- className,
- fieldName
- )?.let { policy ->
- // If the item has an annotation, then use it.
- return policy
+ ) {
+ var count = 0
+ var visibleCount = 0
+ for (an in visibles ?: emptyList()) {
+ if (visibilityAnnotations.contains(an.desc)) {
+ visibleCount++
+ }
+ if (allAnnotations.contains(an.desc)) {
+ count++
+ }
}
- }
- return super.getPolicyForField(className, fieldName)
- }
-
- override fun getPolicyForMethod(
- className: String,
- methodName: String,
- descriptor: String
- ): FilterPolicyWithReason {
- val cn = classes.getClass(className)
-
- if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
- findAnyAnnotation(cn.name, keepStaticInitializerAnnotations,
- cn.visibleAnnotations, cn.invisibleAnnotations)?.let {
- return FilterPolicy.Keep.withReason(reasonAnnotation)
+ for (an in invisibles ?: emptyList()) {
+ if (visibilityAnnotations.contains(an.desc)) {
+ visibleCount++
+ }
+ if (allAnnotations.contains(an.desc)) {
+ count++
+ }
}
- }
-
- cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
- // @SubstituteWith is going to complicate the policy here, so we ask helper
- // what to do.
- substitutionHelper.getPolicyFromSubstitution(cn, mn.name, mn.desc)?.let {
- return it
+ if (count > 0 && !allowAnnotation) {
+ throw InvalidAnnotationException(
+ "Class ${className.toHumanReadableClassName()} is not allowed to have " +
+ "Ravenwood annotations. Contact g/ravenwood for more details."
+ )
}
-
- // If there's no substitution, then we check the annotation.
- findAnnotation(
- cn.name,
- mn.visibleAnnotations,
- mn.invisibleAnnotations,
- "method",
- className,
- methodName,
- descriptor
- )?.let { policy ->
- return policy
+ if (visibleCount > 1) {
+ val description = if (name2 == "" && name3 == "") {
+ "$type $name1"
+ } else {
+ "$type $name1.$name2$name3"
+ }
+ throw InvalidAnnotationException(
+ "Found more than one visibility annotations on $description"
+ )
}
}
- return super.getPolicyForMethod(className, methodName, descriptor)
- }
-
- override fun getRenameTo(
- className: String,
- methodName: String,
- descriptor: String
- ): String? {
- val cn = classes.getClass(className)
-
- // If the method has a "substitute with" annotation, then return its "value" parameter.
- cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
- return substitutionHelper.getRenameTo(cn, mn.name, mn.desc)
- }
- return null
- }
-
- override fun getNativeSubstitutionClass(className: String): String? {
- classes.getClass(className).let { cn ->
- findAnyAnnotation(nativeSubstituteAnnotations,
- cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an ->
- return getAnnotationField(an, "value")?.toJvmClassName()
- }
- }
- return null
- }
-
- override fun getClassLoadHooks(className: String): List<String> {
- val e = classes.getClass(className).let { cn ->
- findAnyAnnotation(classLoadHookAnnotations,
- cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an ->
- getAnnotationField(an, "value")?.toHumanReadableMethodName()
- }
- }
- return addNonNullElement(super.getClassLoadHooks(className), e)
- }
-
- private data class MethodKey(val name: String, val desc: String)
-
- /**
- * In order to handle substitution, we need to build a reverse mapping of substitution
- * methods.
- *
- * This class automatically builds such a map internally that the above methods can
- * take advantage of.
- */
- private inner class SubstitutionHelper {
- private var currentClass: ClassNode? = null
-
- private var policiesFromSubstitution = mutableMapOf<MethodKey, FilterPolicyWithReason>()
- private var substituteToMethods = mutableMapOf<MethodKey, String>()
-
- fun getPolicyFromSubstitution(cn: ClassNode, methodName: String, descriptor: String):
- FilterPolicyWithReason? {
- setClass(cn)
- return policiesFromSubstitution[MethodKey(methodName, descriptor)]
- }
-
- fun getRenameTo(cn: ClassNode, methodName: String, descriptor: String): String? {
- setClass(cn)
- return substituteToMethods[MethodKey(methodName, descriptor)]
- }
/**
- * Every time we see a different class, we scan all its methods for substitution attributes,
- * and compute (implicit) policies caused by them.
- *
- * For example, for the following methods:
- *
- * @Stub
- * @Substitute(suffix = "_host")
- * private void foo() {
- * // This isn't supported on the host side.
- * }
- * private void foo_host() {
- * // Host side implementation
- * }
- *
- * We internally handle them as:
- *
- * foo() -> Remove
- * foo_host() -> Stub, and then rename it to foo().
+ * Return the (String) value of 'value' parameter from an annotation.
*/
- private fun setClass(cn: ClassNode) {
- if (currentClass == cn) {
- return
- }
- // If the class is changing, we'll rebuild the internal structure.
- currentClass = cn
-
- policiesFromSubstitution.clear()
- substituteToMethods.clear()
-
- for (mn in cn.methods ?: emptyList()) {
- findAnyAnnotation(substituteAnnotations,
- mn.visibleAnnotations,
- mn.invisibleAnnotations)?.let { an ->
-
- // Find the policy for this method.
- val policy = outermostFilter.getPolicyForMethod(cn.name, mn.name, mn.desc)
- .policy.resolveClassWidePolicy()
- // Make sure it's either Stub or Keep.
- if (!(policy.needsInStub || policy.needsInImpl)) {
- // TODO: Use the real annotation names in the message
- errors.onErrorFound("@SubstituteWith must have either @Stub or @Keep")
- return@let
- }
- if (!policy.isUsableWithMethods) {
- throw HostStubGenInternalException("Policy $policy shouldn't show up here")
- }
-
- val suffix = getAnnotationField(an, "suffix", false) ?: "\$ravenwood"
- val renameFrom = mn.name + suffix
- val renameTo = mn.name
-
- if (renameFrom == renameTo) {
- errors.onErrorFound("@SubstituteWith have a different name")
- return@let
- }
-
- // This mn has "SubstituteWith". This means,
- // 1. Re move the "rename-to" method, so add it to substitutedMethods.
- policiesFromSubstitution[MethodKey(renameTo, mn.desc)] =
- FilterPolicy.Remove.withReason("substitute-to")
-
- // If the policy is "stub", use "stub".
- // Otherwise, it must be "keep" or "throw", but there's no point in using
- // "throw", so let's use "keep".
- val newPolicy = if (policy.needsInStub) policy else FilterPolicy.Keep
- // 2. We also keep the from-to in the map.
- policiesFromSubstitution[MethodKey(renameFrom, mn.desc)] =
- newPolicy.withReason("substitute-from")
- substituteToMethods[MethodKey(renameFrom, mn.desc)] = renameTo
-
- log.v("Substitution found: %s%s -> %s", renameFrom, mn.desc, renameTo)
+ private fun getAnnotationField(
+ an: AnnotationNode,
+ name: String,
+ required: Boolean = true
+ ): String? {
+ try {
+ val suffix = findAnnotationValueAsString(an, name)
+ if (suffix == null && required) {
+ errors.onErrorFound("Annotation \"${an.desc}\" must have field $name")
}
+ return suffix
+ } catch (e: ClassParseException) {
+ errors.onErrorFound(e.message!!)
+ return null
}
}
- }
- /**
- * Return the (String) value of 'value' parameter from an annotation.
- */
- private fun getAnnotationField(an: AnnotationNode, name: String,
- required: Boolean = true): String? {
- try {
- val suffix = findAnnotationValueAsString(an, name)
- if (suffix == null && required) {
- errors.onErrorFound("Annotation \"${an.desc}\" must have field $name")
- }
- return suffix
- } catch (e: ClassParseException) {
- errors.onErrorFound(e.message!!)
- return null
+ /**
+ * Resolve the full class name if the class is relative
+ */
+ private fun resolveRelativeClass(
+ cn: ClassNode,
+ name: String
+ ): String {
+ val packageName = getPackageNameFromFullClassName(cn.name)
+ return resolveClassNameWithDefaultPackage(name, packageName).toJvmClassName()
}
}
companion object {
+ private const val REASON_ANNOTATION = "annotation"
+ private const val REASON_CLASS_ANNOTATION = "class-annotation"
+
/**
* Convert from human-readable type names (e.g. "com.android.TypeName") to the internal type
* names (e.g. "Lcom/android/TypeName).
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
index 47790b1..f8bb526 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
@@ -16,7 +16,6 @@
package com.android.hoststubgen.filters
import com.android.hoststubgen.asm.ClassNodes
-import com.android.hoststubgen.asm.getDirectOuterClassName
/**
* This is used as the second last fallback filter. This filter propagates the class-wide policy
@@ -24,74 +23,71 @@
*/
class ClassWidePolicyPropagatingFilter(
private val classes: ClassNodes,
- fallback: OutputFilter,
- ) : DelegatingFilter(fallback) {
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
- private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? {
+ /**
+ * We don't use ClassNode.outerClass, because it gives as the top-level
+ * outer class (A$B$C -> A), not the direct outer class (A$B$C -> A$B).
+ *
+ * Sometimes a class name includes `$`, but is not as a nested class name separator
+ * (e.g. a class name like `MyClass$$`). In this case, `MyClass$` is not actually a class.
+ *
+ * So before getting the class policy on a nonexistent class, which may cause an
+ * incorrect result, we make sure the class actually exists.
+ */
+ private fun getDirectOuterClass(className: String): String? {
var currentClass = className
-
-
- // If the class name is `a.b.c.A$B$C`, then we try to get the class wide policy
- // from a.b.c.A$B$C, then a.b.c.A$B, and then a.b.c.A.
while (true) {
- // Sometimes a class name has a `$` in it but not as a nest class name separator --
- // e.g. class name like "MyClass$$". In this case, `MyClass$` may not actually be
- // a class name.
- // So before getting the class policy on a nonexistent class, which may cause an
- // incorrect result, we make sure if className actually exists.
- if (classes.hasClass(className)) {
- outermostFilter.getPolicyForClass(className).let { policy ->
- if (policy.policy.isClassWidePolicy) {
- val p = if (resolve) {
- policy.policy.resolveClassWidePolicy()
- } else {
- policy.policy
- }
-
- return p.withReason(policy.reason)
- .wrapReason("class-wide in $currentClass")
- }
- // If the class's policy is remove, then remove it.
- if (policy.policy == FilterPolicy.Remove) {
- return FilterPolicy.Remove.withReason("class-wide in $currentClass")
- }
- }
- }
-
- // Next, look at the outer class...
- val outer = getDirectOuterClassName(currentClass)
- if (outer == null) {
+ val pos = currentClass.lastIndexOf('$')
+ if (pos < 0) {
return null
}
- currentClass = outer
+ currentClass = currentClass.substring(0, pos)
+ if (classes.hasClass(currentClass)) {
+ return currentClass
+ }
}
}
+ private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? {
+ outermostFilter.getPolicyForClass(className).let { policy ->
+ if (policy.policy == FilterPolicy.KeepClass) {
+ val p = if (resolve) {
+ policy.policy.resolveClassWidePolicy()
+ } else {
+ policy.policy
+ }
+
+ return p.withReason(policy.reason)
+ .wrapReason("class-wide in $className")
+ }
+ // If the class's policy is remove, then remove it.
+ if (policy.policy == FilterPolicy.Remove) {
+ return FilterPolicy.Remove.withReason("class-wide in $className")
+ }
+ }
+ return null
+ }
+
override fun getPolicyForClass(className: String): FilterPolicyWithReason {
- // If it's a nested class, use the outer class's policy.
- getDirectOuterClassName(className)?.let { outerName ->
- getClassWidePolicy(outerName, resolve = false)?.let { policy ->
- return policy
- }
- }
-
- return super.getPolicyForClass(className)
+ // If the class name is `a.b.c.A$B$C`, then we try to get the class wide policy
+ // from a.b.c.A$B$C, then a.b.c.A$B, and then a.b.c.A, recursively
+ return getDirectOuterClass(className)?.let { getClassWidePolicy(it, resolve = false) }
+ ?: super.getPolicyForClass(className)
}
- override fun getPolicyForField(
- className: String,
- fieldName: String
- ): FilterPolicyWithReason {
+ override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
return getClassWidePolicy(className, resolve = true)
?: super.getPolicyForField(className, fieldName)
}
override fun getPolicyForMethod(
- className: String,
- methodName: String,
- descriptor: String
+ className: String,
+ methodName: String,
+ descriptor: String
): FilterPolicyWithReason {
return getClassWidePolicy(className, resolve = true)
- ?: super.getPolicyForMethod(className, methodName, descriptor)
+ ?: super.getPolicyForMethod(className, methodName, descriptor)
}
-}
\ No newline at end of file
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
index 678e6ea..be3c59c 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
@@ -26,23 +26,17 @@
* @param policy the policy. Cannot be a "substitute" policy.
*/
class ConstantFilter(
- policy: FilterPolicy,
- val reason: String
+ policy: FilterPolicy,
+ private val reason: String
) : OutputFilter() {
- val classPolicy: FilterPolicy
- val fieldPolicy: FilterPolicy
- val methodPolicy: FilterPolicy
+
+ private val classPolicy: FilterPolicy
+ private val fieldPolicy: FilterPolicy
+ private val methodPolicy: FilterPolicy
init {
- if (policy.isSubstitute) {
- throw HostStubGenInternalException(
- "ConstantFilter doesn't allow substitution policies.")
- }
- if (policy.isClassWidePolicy) {
- // We prevent it, because there's no point in using class-wide policies because
- // all members get othe same policy too anyway.
- throw HostStubGenInternalException(
- "ConstantFilter doesn't allow class-wide policies.")
+ if (!policy.isUsableWithDefault) {
+ throw HostStubGenInternalException("ConstantFilter doesn't support $policy.")
}
methodPolicy = policy
@@ -63,10 +57,10 @@
}
override fun getPolicyForMethod(
- className: String,
- methodName: String,
- descriptor: String,
- ): FilterPolicyWithReason {
+ className: String,
+ methodName: String,
+ descriptor: String,
+ ): FilterPolicyWithReason {
return methodPolicy.withReason(reason)
}
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
index 6fcffb8..b8b0d8a 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
@@ -72,8 +72,8 @@
return fallback.getRenameTo(className, methodName, descriptor)
}
- override fun getNativeSubstitutionClass(className: String): String? {
- return fallback.getNativeSubstitutionClass(className)
+ override fun getRedirectionClass(className: String): String? {
+ return fallback.getRedirectionClass(className)
}
override fun getClassLoadHooks(className: String): List<String> {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
index f839444..2f2f81b 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
@@ -17,37 +17,25 @@
enum class FilterPolicy {
/**
- * Keep the item in the stub jar file, so tests can use it.
- */
- Stub,
-
- /**
- * Keep the item in the impl jar file, but not in the stub file. Tests cannot use it directly,
- * but indirectly.
+ * Keep the item in the jar file.
*/
Keep,
/**
- * Only used for types. Keep the class in the stub, and also all its members.
- * But each member can have another annotations to override it.
- */
- StubClass,
-
- /**
- * Only used for types. Keep the class in the impl, not in the stub, and also all its members.
- * But each member can have another annotations to override it.
+ * Only usable with classes. Keep the class in the jar, and also all its members.
+ * Each member can have another policy to override it.
*/
KeepClass,
/**
- * Same as [Stub], but replace it with a "substitution" method. Only usable with methods.
+ * Only usable with methods. Replace a method with a "substitution" method.
*/
- SubstituteAndStub,
+ Substitute,
/**
- * Same as [Keep], but replace it with a "substitution" method. Only usable with methods.
+ * Only usable with methods. Redirect a method to a method in the substitution class.
*/
- SubstituteAndKeep,
+ Redirect,
/**
* Only usable with methods. The item will be kept in the impl jar file, but when called,
@@ -57,7 +45,7 @@
/**
* Only usable with methods. The item will be kept in the impl jar file, but when called,
- * it'll no-op. Currently only supported for methods returning `void`.
+ * it'll no-op.
*/
Ignore,
@@ -66,20 +54,19 @@
*/
Remove;
- val isSubstitute: Boolean
- get() = this == SubstituteAndStub || this == SubstituteAndKeep
-
- val needsInStub: Boolean
- get() = this == Stub || this == StubClass || this == SubstituteAndStub || this == Ignore
-
- val needsInImpl: Boolean
- get() = this != Remove
+ val needsInOutput: Boolean
+ get() {
+ return when (this) {
+ Remove -> false
+ else -> true
+ }
+ }
/** Returns whether a policy can be used with classes */
val isUsableWithClasses: Boolean
get() {
return when (this) {
- Stub, StubClass, Keep, KeepClass, Remove -> true
+ Keep, KeepClass, Remove -> true
else -> false
}
}
@@ -88,7 +75,7 @@
val isUsableWithFields: Boolean
get() {
return when (this) {
- Stub, Keep, Remove -> true
+ Keep, Remove -> true
else -> false
}
}
@@ -97,16 +84,16 @@
val isUsableWithMethods: Boolean
get() {
return when (this) {
- StubClass, KeepClass -> false
+ KeepClass -> false
else -> true
}
}
- /** Returns whether a policy is a class-wide one. */
- val isClassWidePolicy: Boolean
+ /** Returns whether a policy can be used as default policy. */
+ val isUsableWithDefault: Boolean
get() {
return when (this) {
- StubClass, KeepClass -> true
+ Keep, Throw, Remove -> true
else -> false
}
}
@@ -115,26 +102,24 @@
val isSupported: Boolean
get() {
return when (this) {
- // TODO: handle native method with no substitution as being unsupported
- Stub, StubClass, Keep, KeepClass, SubstituteAndStub, SubstituteAndKeep -> true
+ Keep, KeepClass, Substitute, Redirect -> true
else -> false
}
}
- fun getSubstitutionBasePolicy(): FilterPolicy {
- return when (this) {
- SubstituteAndKeep -> Keep
- SubstituteAndStub -> Stub
- else -> this
+ val isMethodRewriteBody: Boolean
+ get() {
+ return when (this) {
+ Redirect, Throw, Ignore -> true
+ else -> false
+ }
}
- }
/**
- * Convert {Stub,Keep}Class to the corresponding Stub or Keep.
+ * Convert KeepClass to Keep, or return itself.
*/
fun resolveClassWidePolicy(): FilterPolicy {
return when (this) {
- StubClass -> Stub
KeepClass -> Keep
else -> this
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
index eb03f66..b10165b 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
@@ -30,36 +30,6 @@
return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]")
}
- /**
- * If the visibility is lower than "Keep" (meaning if it's "remove"),
- * then return a new [FilterPolicy] with "Keep".
- * Otherwise, return itself
- */
- fun promoteToKeep(promotionReason: String): FilterPolicyWithReason {
- if (policy.needsInImpl) {
- return this
- }
- val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep
-
- return FilterPolicyWithReason(newPolicy,
- "$promotionReason [original remove reason: ${this.reason}]")
- }
-
- /**
- * If the visibility is above "Keep" (meaning if it's "stub"),
- * then return a new [FilterPolicy] with "Keep".
- * Otherwise, return itself
- */
- fun demoteToKeep(promotionReason: String): FilterPolicyWithReason {
- if (!policy.needsInStub) {
- return this
- }
- val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep
-
- return FilterPolicyWithReason(newPolicy,
- "$promotionReason [original stub reason: ${this.reason}]")
- }
-
override fun toString(): String {
return "[$policy - reason: $reason]"
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 5a26fc6..474da6d 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -25,7 +25,6 @@
import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
import com.android.hoststubgen.asm.isEnum
import com.android.hoststubgen.asm.isSynthetic
-import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
import com.android.hoststubgen.log
import org.objectweb.asm.tree.ClassNode
@@ -53,7 +52,7 @@
}
// If the outer class needs to be in impl, it should be in impl too.
val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass)
- if (outerPolicy.policy.needsInImpl) {
+ if (outerPolicy.policy.needsInOutput) {
return FilterPolicy.KeepClass.withReason("anonymous-inner-class")
}
}
@@ -79,19 +78,6 @@
val fallback = super.getPolicyForMethod(className, methodName, descriptor)
val classPolicy = outermostFilter.getPolicyForClass(className)
- // If the class is in the stub, then we need to put the private constructor in the stub too,
- // to prevent the class from getting instantiated.
- if (classPolicy.policy.needsInStub &&
- !fallback.policy.needsInStub &&
- (methodName == "<init>") && // Constructor?
- (descriptor == "()V")) { // Has zero parameters?
- classes.findMethod(className, methodName, descriptor)?.let { mn ->
- if (isVisibilityPrivateOrPackagePrivate(mn.access)) {
- return FilterPolicy.Stub.withReason("private constructor in stub class")
- }
- }
- }
-
val cn = classes.getClass(className)
// If we throw from the static initializer, the class would be useless, so we convert it
@@ -107,7 +93,7 @@
}
log.d("Class ${cn.name} Class policy: $classPolicy")
- if (classPolicy.policy.needsInImpl) {
+ if (classPolicy.policy.needsInOutput) {
// Do it only when the class needs to be kept...
// Member policy should be "keep" or "stub".
@@ -152,7 +138,7 @@
val classPolicy = outermostFilter.getPolicyForClass(className)
log.d("Class ${cn.name} Class policy: $classPolicy")
- if (classPolicy.policy.needsInImpl) {
+ if (classPolicy.policy.needsInOutput) {
// Do it only when the class needs to be kept...
// Member policy should be "keep" or "stub".
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index 2e144f5..59fa464 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -19,6 +19,7 @@
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.asm.toHumanReadableMethodName
+import com.android.hoststubgen.asm.toJvmClassName
import com.android.hoststubgen.log
// TODO: Validate all input names.
@@ -29,7 +30,7 @@
) : DelegatingFilter(fallback) {
private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf()
private val mRenames: MutableMap<String, String> = mutableMapOf()
- private val mNativeSubstitutionClasses: MutableMap<String, String> = mutableMapOf()
+ private val mRedirectionClasses: MutableMap<String, String> = mutableMapOf()
private val mClassLoadHooks: MutableMap<String, String> = mutableMapOf()
private fun getClassKey(className: String): String {
@@ -115,17 +116,17 @@
mRenames[getMethodKey(className, methodName, descriptor)] = toName
}
- override fun getNativeSubstitutionClass(className: String): String? {
- return mNativeSubstitutionClasses[getClassKey(className)]
- ?: super.getNativeSubstitutionClass(className)
+ override fun getRedirectionClass(className: String): String? {
+ return mRedirectionClasses[getClassKey(className)]
+ ?: super.getRedirectionClass(className)
}
- fun setNativeSubstitutionClass(from: String, to: String) {
+ fun setRedirectionClass(from: String, to: String) {
checkClass(from)
- // Native substitute classes may be provided from other jars, so we can't do this check.
+ // Redirection classes may be provided from other jars, so we can't do this check.
// ensureClassExists(to)
- mNativeSubstitutionClasses[getClassKey(from)] = to.toHumanReadableClassName()
+ mRedirectionClasses[getClassKey(from)] = to.toJvmClassName()
}
override fun getClassLoadHooks(className: String): List<String> {
@@ -136,4 +137,4 @@
fun setClassLoadHook(className: String, methodName: String) {
mClassLoadHooks[getClassKey(className)] = methodName.toHumanReadableMethodName()
}
-}
\ No newline at end of file
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
new file mode 100644
index 0000000..00e7d77
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.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.hoststubgen.filters
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.isNative
+
+/**
+ * For native methods that weren't handled by outer filters, we keep it so that
+ * native method registration will not crash at runtime. Ideally we shouldn't need
+ * this, but in practice unsupported native method registrations do occur.
+ */
+class KeepNativeFilter(
+ private val classes: ClassNodes,
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String,
+ ): FilterPolicyWithReason {
+ return classes.findMethod(className, methodName, descriptor)?.let { mn ->
+ if (mn.isNative()) {
+ FilterPolicy.Keep.withReason("native-preserve")
+ } else {
+ null
+ }
+ } ?: super.getPolicyForMethod(className, methodName, descriptor)
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
index 1049e2b..f99ce90 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
@@ -35,10 +35,6 @@
* using it.
*/
open var outermostFilter: OutputFilter = this
- get() = field
- set(value) {
- field = value
- }
abstract fun getPolicyForClass(className: String): FilterPolicyWithReason
@@ -60,13 +56,13 @@
}
/**
- * Return a "native substitution class" name for a given class.
+ * Return a "redirection class" name for a given class.
*
- * The result will be in a "human readable" form. (e.g. uses '.'s instead of '/'s)
+ * The result will be in a JVM internal form. (e.g. uses '/'s instead of '.'s)
*
- * (which corresponds to @HostSideTestNativeSubstitutionClass of the standard annotations.)
+ * (which corresponds to @HostSideTestRedirectClass of the standard annotations.)
*/
- open fun getNativeSubstitutionClass(className: String): String? {
+ open fun getRedirectionClass(className: String): String? {
return null
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
new file mode 100644
index 0000000..18a1e16
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.hoststubgen.filters
+
+import com.android.hoststubgen.HostStubGenErrors
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.log
+
+/**
+ * Check whether the policies in the inner layers make sense, and sanitize the results.
+ */
+class SanitizationFilter(
+ private val errors: HostStubGenErrors,
+ private val classes: ClassNodes,
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): FilterPolicyWithReason {
+ val policy = super.getPolicyForMethod(className, methodName, descriptor)
+ if (policy.policy == FilterPolicy.Redirect) {
+ // Check whether the hosting class has a redirection class
+ if (getRedirectionClass(className) == null) {
+ errors.onErrorFound("Method $methodName$descriptor requires a redirection " +
+ "class set on ${className.toHumanReadableClassName()}")
+ }
+ }
+ return policy
+ }
+
+ override fun getRedirectionClass(className: String): String? {
+ return super.getRedirectionClass(className)?.also { clazz ->
+ if (classes.findClass(clazz) == null) {
+ log.w("Redirection class $clazz not found. Class must be available at runtime.")
+ } else if (outermostFilter.getPolicyForClass(clazz).policy != FilterPolicy.KeepClass) {
+ // If the class exists, it must have a KeepClass policy.
+ errors.onErrorFound("Redirection class $clazz must have @KeepWholeClass.")
+ }
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt
deleted file mode 100644
index f92a027..0000000
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2023 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.hoststubgen.filters
-
-import com.android.hoststubgen.HostStubGenErrors
-import com.android.hoststubgen.asm.ClassNodes
-
-private const val REASON = "demoted, not in intersect jars"
-
-/**
- * An [OutputFilter] that will restrict what to put in stub to only what shows up in "intersecting
- * jar" files.
- *
- * For example, if the Android public API stub jar is provided, then the HostStubGen's output
- * stub will be restricted to public APIs.
- */
-class StubIntersectingFilter(
- private val errors: HostStubGenErrors,
- /**
- * If a class / field / method is not in any of these jars, then we will not put it in
- * stub.
- */
- private val intersectingJars: Map<String, ClassNodes>,
- fallback: OutputFilter,
-) : DelegatingFilter(fallback) {
- private inline fun exists(predicate: (ClassNodes) -> Boolean): Boolean {
- intersectingJars.forEach { entry ->
- if (predicate(entry.value)) {
- return true
- }
- }
- return false
- }
-
- /**
- * If [origPolicy] is less than "Stub", then return it as-is.
- *
- * Otherwise, call [inStubChecker] to see if the API is in any of [intersectingJars].
- * If yes, then return [origPolicy] as-is. Otherwise, demote to "Keep".
- */
- private fun intersectWithStub(
- origPolicy: FilterPolicyWithReason,
- inStubChecker: () -> Boolean,
- ): FilterPolicyWithReason {
- if (origPolicy.policy.needsInStub) {
- // Only check the stub jars, when the class is supposed to be in stub otherwise.
- if (!inStubChecker()) {
- return origPolicy.demoteToKeep(REASON)
- }
- }
- return origPolicy
- }
-
- override fun getPolicyForClass(className: String): FilterPolicyWithReason {
- return intersectWithStub(super.getPolicyForClass(className)) {
- exists { classes -> classes.findClass(className) != null }
- }
- }
-
- override fun getPolicyForField(
- className: String,
- fieldName: String
- ): FilterPolicyWithReason {
- return intersectWithStub(super.getPolicyForField(className, fieldName)) {
- exists { classes -> classes.findField(className, fieldName) != null }
- }
- }
-
- override fun getPolicyForMethod(
- className: String,
- methodName: String,
- descriptor: String
- ): FilterPolicyWithReason {
- return intersectWithStub(super.getPolicyForMethod(className, methodName, descriptor)) {
- exists { classes -> classes.findMethod(className, methodName, descriptor) != null }
- }
- }
-}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 53bcf10..073b503 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -142,9 +142,9 @@
throw ParseException(
"Special class can't have a substitution")
}
- // It's a native-substitution.
+ // It's a redirection class.
val toClass = fields[2].substring(1)
- imf.setNativeSubstitutionClass(className, toClass)
+ imf.setRedirectionClass(className, toClass)
} else if (fields[2].startsWith("~")) {
if (classType != SpecialClass.NotSpecial) {
// We could support it, but not needed at least for now.
@@ -240,7 +240,7 @@
imf.setPolicyForMethod(className, name, signature,
policy.withReason(FILTER_REASON))
- if (policy.isSubstitute) {
+ if (policy == FilterPolicy.Substitute) {
val fromName = fields[3].substring(1)
if (fromName == name) {
@@ -248,10 +248,9 @@
"Substitution must have a different name")
}
- // Set the policy for the "from" method.
+ // Set the policy for the "from" method.
imf.setPolicyForMethod(className, fromName, signature,
- policy.getSubstitutionBasePolicy()
- .withReason(FILTER_REASON))
+ FilterPolicy.Keep.withReason(FILTER_REASON))
val classAndMethod = splitWithLastPeriod(fromName)
if (classAndMethod != null) {
@@ -346,18 +345,15 @@
private fun parsePolicy(s: String): FilterPolicy {
return when (s.lowercase()) {
- "s", "stub" -> FilterPolicy.Stub
"k", "keep" -> FilterPolicy.Keep
"t", "throw" -> FilterPolicy.Throw
"r", "remove" -> FilterPolicy.Remove
- "sc", "stubclass" -> FilterPolicy.StubClass
"kc", "keepclass" -> FilterPolicy.KeepClass
"i", "ignore" -> FilterPolicy.Ignore
+ "rdr", "redirect" -> FilterPolicy.Redirect
else -> {
if (s.startsWith("@")) {
- FilterPolicy.SubstituteAndStub
- } else if (s.startsWith("%")) {
- FilterPolicy.SubstituteAndKeep
+ FilterPolicy.Substitute
} else {
throw ParseException("Invalid policy \"$s\"")
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
index 01a7ab3..7440b94 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
@@ -24,19 +24,19 @@
/**
* General purpose filter for class names.
*/
-class ClassFilter private constructor (
- val defaultResult: Boolean,
+class ClassFilter private constructor(
+ private val defaultResult: Boolean,
) {
- private data class FilterElement(
- val allowed: Boolean,
- val internalName: String,
- val isPrefix: Boolean,
+ private class FilterElement(
+ val allowed: Boolean,
+ val internalName: String,
+ val isPrefix: Boolean,
) {
fun matches(classInternalName: String): Boolean {
- if (isPrefix) {
- return classInternalName.startsWith(internalName)
+ return if (isPrefix) {
+ classInternalName.startsWith(internalName)
} else {
- return classInternalName == internalName
+ classInternalName == internalName
}
}
}
@@ -54,15 +54,16 @@
return it
}
- var result = defaultResult
- run outer@{
- elements.forEach { e ->
- if (e.matches(classInternalName)) {
- result = e.allowed
- return@outer // break equivalent.
- }
+ val testClasses = sequence {
+ // Yield itself and its outer class(es) one by one
+ var idx = classInternalName.length
+ while (idx > 0) {
+ yield(classInternalName.substring(0, idx))
+ idx = classInternalName.lastIndexOf('$', idx - 1)
}
}
+
+ val result = elements.find { testClasses.any(it::matches) }?.allowed ?: defaultResult
cache[classInternalName] = result
return result
@@ -87,9 +88,9 @@
/** Build a filter from a string (for unit tests). */
fun buildFromString(
- filterString: String,
- defaultResult: Boolean,
- filenameForErrorMessage: String
+ filterString: String,
+ defaultResult: Boolean,
+ filenameForErrorMessage: String
): ClassFilter {
val ret = ClassFilter(defaultResult)
@@ -119,17 +120,20 @@
// Handle wildcard -- e.g. "package.name.*"
if (line.endsWith(".*")) {
- ret.elements.add(FilterElement(
- allow, line.substring(0, line.length - 2).toJvmClassName(), true))
+ ret.elements.add(
+ FilterElement(
+ allow, line.substring(0, line.length - 2).toJvmClassName(), true
+ )
+ )
return@forEach
}
// Any other uses of "*" would be an error.
if (line.contains('*')) {
throw ParseException(
- "Wildcard (*) can only show up as the last element",
- filenameForErrorMessage,
- lineNo
+ "Wildcard (*) can only show up as the last element",
+ filenameForErrorMessage,
+ lineNo
)
}
ret.elements.add(FilterElement(allow, line.toJvmClassName(), false))
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
index bad0449..261ef59c 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -21,103 +21,64 @@
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.UnifiedVisitor
import com.android.hoststubgen.asm.getPackageNameFromFullClassName
-import com.android.hoststubgen.asm.resolveClassNameWithDefaultPackage
-import com.android.hoststubgen.asm.toJvmClassName
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.FilterPolicyWithReason
import com.android.hoststubgen.filters.OutputFilter
-import com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-import com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+import com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
import com.android.hoststubgen.log
+import java.io.PrintWriter
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.commons.ClassRemapper
-import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.util.TraceClassVisitor
-import java.io.PrintWriter
-val OPCODE_VERSION = Opcodes.ASM9
+const val OPCODE_VERSION = Opcodes.ASM9
-abstract class BaseAdapter (
- protected val classes: ClassNodes,
- nextVisitor: ClassVisitor,
- protected val filter: OutputFilter,
- protected val options: Options,
+abstract class BaseAdapter(
+ protected val classes: ClassNodes,
+ nextVisitor: ClassVisitor,
+ protected val filter: OutputFilter,
+ protected val options: Options,
) : ClassVisitor(OPCODE_VERSION, nextVisitor) {
/**
* Options to control the behavior.
*/
- data class Options (
- val errors: HostStubGenErrors,
- val stats: HostStubGenStats?,
- val enablePreTrace: Boolean,
- val enablePostTrace: Boolean,
- val enableNonStubMethodCallDetection: Boolean,
- )
+ data class Options(
+ val errors: HostStubGenErrors,
+ val stats: HostStubGenStats?,
+ val enablePreTrace: Boolean,
+ val enablePostTrace: Boolean
+ )
protected lateinit var currentPackageName: String
protected lateinit var currentClassName: String
- protected var nativeSubstitutionClass: String? = null
+ protected var redirectionClass: String? = null
protected lateinit var classPolicy: FilterPolicyWithReason
- /**
- * Return whether an item with a given policy should be included in the output.
- */
- protected abstract fun shouldEmit(policy: FilterPolicy): Boolean
-
- /**
- * Inject [HostStubGenKeptInStub] and [HostStubGenKeptInImpl] as needed to an item.
- */
- protected fun injectInStubAndKeepAnnotations(policy: FilterPolicy, v: UnifiedVisitor) {
- if (policy.needsInStub) {
- v.visitAnnotation(HostStubGenKeptInStub.CLASS_DESCRIPTOR, true)
- }
- if (policy.needsInImpl) {
- v.visitAnnotation(HostStubGenKeptInImpl.CLASS_DESCRIPTOR, true)
- }
- }
-
override fun visit(
- version: Int,
- access: Int,
- name: String,
- signature: String?,
- superName: String?,
- interfaces: Array<String>,
+ version: Int,
+ access: Int,
+ name: String,
+ signature: String?,
+ superName: String?,
+ interfaces: Array<String>,
) {
super.visit(version, access, name, signature, superName, interfaces)
currentClassName = name
currentPackageName = getPackageNameFromFullClassName(name)
classPolicy = filter.getPolicyForClass(currentClassName)
+ redirectionClass = filter.getRedirectionClass(currentClassName)
log.d("[%s] visit: %s (package: %s)", this.javaClass.simpleName, name, currentPackageName)
log.indent()
log.v("Emitting class: %s", name)
log.indent()
- filter.getNativeSubstitutionClass(currentClassName)?.let { className ->
- val fullClassName = resolveClassNameWithDefaultPackage(className, currentPackageName)
- .toJvmClassName()
- log.d(" NativeSubstitutionClass: $fullClassName")
- if (classes.findClass(fullClassName) == null) {
- log.w("Native substitution class $fullClassName not found. Class must be " +
- "available at runtime.")
- } else {
- // If the class exists, it must have a KeepClass policy.
- if (filter.getPolicyForClass(fullClassName).policy != FilterPolicy.KeepClass) {
- // TODO: Use real annotation name.
- options.errors.onErrorFound(
- "Native substitution class $fullClassName should have @Keep.")
- }
- }
-
- nativeSubstitutionClass = fullClassName
- }
// Inject annotations to generated classes.
- injectInStubAndKeepAnnotations(classPolicy.policy, UnifiedVisitor.on(this))
+ UnifiedVisitor.on(this).visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true)
}
override fun visitEnd() {
@@ -141,11 +102,11 @@
}
override fun visitField(
- access: Int,
- name: String,
- descriptor: String,
- signature: String?,
- value: Any?,
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ value: Any?,
): FieldVisitor? {
if (skipMemberModificationNestCount > 0) {
return super.visitField(access, name, descriptor, signature, value)
@@ -154,7 +115,7 @@
log.d("visitField: %s %s [%x] Policy: %s", name, descriptor, access, policy)
log.withIndent {
- if (!shouldEmit(policy.policy)) {
+ if (policy.policy == FilterPolicy.Remove) {
log.d("Removing %s %s", name, policy)
return null
}
@@ -162,18 +123,19 @@
log.v("Emitting field: %s %s %s", name, descriptor, policy)
val ret = super.visitField(access, name, descriptor, signature, value)
- injectInStubAndKeepAnnotations(policy.policy, UnifiedVisitor.on(ret))
+ UnifiedVisitor.on(ret)
+ .visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true)
return ret
}
}
override fun visitMethod(
- access: Int,
- name: String,
- descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
): MethodVisitor? {
if (skipMemberModificationNestCount > 0) {
return super.visitMethod(access, name, descriptor, signature, exceptions)
@@ -187,11 +149,11 @@
// Instead of this method, we rename the substitute-to method with the original
// name, in the "Maybe rename the method" part below.
val policy = filter.getPolicyForMethod(currentClassName, name, descriptor)
- if (policy.policy.isSubstitute) {
+ if (policy.policy == FilterPolicy.Substitute) {
log.d("Skipping %s%s %s", name, descriptor, policy)
return null
}
- if (!shouldEmit(p.policy)) {
+ if (p.policy == FilterPolicy.Remove) {
log.d("Removing %s%s %s", name, descriptor, policy)
return null
}
@@ -209,13 +171,16 @@
// `name` is the name of the method we're currently visiting, so it's usually a
// "...$ravewnwood" name.
newAccess = checkSubstitutionMethodCompatibility(
- classes, currentClassName, newName, name, descriptor, options.errors)
+ classes, currentClassName, newName, name, descriptor, options.errors
+ )
if (newAccess == NOT_COMPATIBLE) {
return null
}
- log.v("Emitting %s.%s%s as %s %s", currentClassName, name, descriptor,
- newName, policy)
+ log.v(
+ "Emitting %s.%s%s as %s %s", currentClassName, name, descriptor,
+ newName, policy
+ )
} else {
log.v("Emitting method: %s%s %s", name, descriptor, policy)
newName = name
@@ -225,14 +190,17 @@
// But note, we only use it when calling the super's method,
// but not for visitMethodInner(), because when subclass wants to change access,
// it can do so inside visitMethodInner().
- newAccess = updateAccessFlags(newAccess, name, descriptor)
+ newAccess = updateAccessFlags(newAccess, name, descriptor, policy.policy)
- val ret = visitMethodInner(access, newName, descriptor, signature, exceptions, policy,
+ val ret = visitMethodInner(
+ access, newName, descriptor, signature, exceptions, policy,
renameTo != null,
- super.visitMethod(newAccess, newName, descriptor, signature, exceptions))
+ super.visitMethod(newAccess, newName, descriptor, signature, exceptions)
+ )
ret?.let {
- injectInStubAndKeepAnnotations(policy.policy, UnifiedVisitor.on(ret))
+ UnifiedVisitor.on(ret)
+ .visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true)
}
return ret
@@ -240,9 +208,10 @@
}
open fun updateAccessFlags(
- access: Int,
- name: String,
- descriptor: String,
+ access: Int,
+ name: String,
+ descriptor: String,
+ policy: FilterPolicy,
): Int {
return access
}
@@ -256,7 +225,7 @@
policy: FilterPolicyWithReason,
substituted: Boolean,
superVisitor: MethodVisitor?,
- ): MethodVisitor?
+ ): MethodVisitor?
companion object {
fun getVisitor(
@@ -265,8 +234,6 @@
nextVisitor: ClassVisitor,
filter: OutputFilter,
packageRedirector: PackageRedirectRemapper,
- remapper: Remapper?,
- forImpl: Boolean,
options: Options,
): ClassVisitor {
var next = nextVisitor
@@ -289,23 +256,20 @@
if (!packageRedirector.isTarget(classInternalName)) {
next = ClassRemapper(next, packageRedirector)
} else {
- log.v("Class $classInternalName is a redirect-from class, not applying" +
- " --package-redirect")
+ log.v(
+ "Class $classInternalName is a redirect-from class, not applying" +
+ " --package-redirect"
+ )
}
}
- var ret: ClassVisitor
- if (forImpl) {
- ret = ImplGeneratingAdapter(classes, next, filter, options)
- } else {
- ret = StubGeneratingAdapter(classes, next, filter, options)
- }
+ next = ImplGeneratingAdapter(classes, next, filter, options)
// Inject TraceClassVisitor for debugging.
if (options.enablePreTrace) {
- ret = TraceClassVisitor(ret, verbosePrinter)
+ next = TraceClassVisitor(next, verbosePrinter)
}
- return ret
+ return next
}
}
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
index 8250412..55d0c0e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
@@ -20,38 +20,23 @@
import org.objectweb.asm.Handle
import org.objectweb.asm.Label
import org.objectweb.asm.MethodVisitor
-import org.objectweb.asm.Opcodes
import org.objectweb.asm.TypePath
/**
- * A method visitor that removes everything from method body.
+ * A method visitor that creates or replaces a method body.
*
- * To inject a method body, override [visitCode] and create the opcodes there.
+ * Override [emitNewCode] to build the method body.
*/
abstract class BodyReplacingMethodVisitor(
- access: Int,
- name: String,
- descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- next: MethodVisitor?,
+ private val createBody: Boolean,
+ next: MethodVisitor?
) : MethodVisitor(OPCODE_VERSION, next) {
- val isVoid: Boolean
- val isStatic: Boolean
-
- init {
- isVoid = descriptor.endsWith(")V")
- isStatic = access and Opcodes.ACC_STATIC != 0
- }
// Following methods are for things that we need to keep.
// Since they're all calling the super method, we can just remove them, but we keep them
// just to clarify what we're keeping.
- final override fun visitParameter(
- name: String?,
- access: Int
- ) {
+ final override fun visitParameter(name: String?, access: Int) {
super.visitParameter(name, access)
}
@@ -59,10 +44,7 @@
return super.visitAnnotationDefault()
}
- final override fun visitAnnotation(
- descriptor: String?,
- visible: Boolean
- ): AnnotationVisitor? {
+ final override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
return super.visitAnnotation(descriptor, visible)
}
@@ -75,17 +57,14 @@
return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible)
}
- final override fun visitAnnotableParameterCount(
- parameterCount: Int,
- visible: Boolean
- ) {
+ final override fun visitAnnotableParameterCount(parameterCount: Int, visible: Boolean) {
super.visitAnnotableParameterCount(parameterCount, visible)
}
final override fun visitParameterAnnotation(
- parameter: Int,
- descriptor: String?,
- visible: Boolean
+ parameter: Int,
+ descriptor: String?,
+ visible: Boolean
): AnnotationVisitor? {
return super.visitParameterAnnotation(parameter, descriptor, visible)
}
@@ -94,10 +73,6 @@
super.visitAttribute(attribute)
}
- override fun visitEnd() {
- super.visitEnd()
- }
-
/**
* Control when to emit the code. We use this to ignore all visitXxx method calls caused by
* the original method, so we'll remove all the original code.
@@ -108,9 +83,18 @@
* (See also https://asm.ow2.io/asm4-guide.pdf section 3.2.1 about the MethovVisitor
* call order.)
*/
- var emitCode = false
+ private var emitCode = false
+
+ /**
+ * This value will be set as true when [visitCode] is called. In [visitEnd], if this value
+ * is still false, this means that the original method does not have a body.
+ *
+ * We want to forcefully inject a method body in [visitEnd] if [createBody] is true.
+ */
+ private var visitedCode = false
final override fun visitCode() {
+ visitedCode = true
super.visitCode()
try {
@@ -122,15 +106,19 @@
}
}
+ final override fun visitEnd() {
+ if (!visitedCode && createBody) {
+ visitCode()
+ }
+ super.visitEnd()
+ }
+
/**
* Subclass must implement it and emit code, and call [visitMaxs] at the end.
*/
abstract fun emitNewCode()
- final override fun visitMaxs(
- maxStack: Int,
- maxLocals: Int
- ) {
+ final override fun visitMaxs(maxStack: Int, maxLocals: Int) {
if (emitCode) {
super.visitMaxs(maxStack, maxLocals)
}
@@ -140,11 +128,11 @@
// emit any of them, so they are all no-op.
final override fun visitFrame(
- type: Int,
- numLocal: Int,
- local: Array<out Any>?,
- numStack: Int,
- stack: Array<out Any>?
+ type: Int,
+ numLocal: Int,
+ local: Array<out Any>?,
+ numStack: Int,
+ stack: Array<out Any>?
) {
if (emitCode) {
super.visitFrame(type, numLocal, local, numStack, stack)
@@ -157,38 +145,29 @@
}
}
- final override fun visitIntInsn(
- opcode: Int,
- operand: Int
- ) {
+ final override fun visitIntInsn(opcode: Int, operand: Int) {
if (emitCode) {
super.visitIntInsn(opcode, operand)
}
}
- final override fun visitVarInsn(
- opcode: Int,
- varIndex: Int
- ) {
+ final override fun visitVarInsn(opcode: Int, varIndex: Int) {
if (emitCode) {
super.visitVarInsn(opcode, varIndex)
}
}
- final override fun visitTypeInsn(
- opcode: Int,
- type: String?
- ) {
+ final override fun visitTypeInsn(opcode: Int, type: String?) {
if (emitCode) {
super.visitTypeInsn(opcode, type)
}
}
final override fun visitFieldInsn(
- opcode: Int,
- owner: String?,
- name: String?,
- descriptor: String?
+ opcode: Int,
+ owner: String?,
+ name: String?,
+ descriptor: String?
) {
if (emitCode) {
super.visitFieldInsn(opcode, owner, name, descriptor)
@@ -196,11 +175,11 @@
}
final override fun visitMethodInsn(
- opcode: Int,
- owner: String?,
- name: String?,
- descriptor: String?,
- isInterface: Boolean
+ opcode: Int,
+ owner: String?,
+ name: String?,
+ descriptor: String?,
+ isInterface: Boolean
) {
if (emitCode) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
@@ -208,21 +187,20 @@
}
final override fun visitInvokeDynamicInsn(
- name: String?,
- descriptor: String?,
- bootstrapMethodHandle: Handle?,
- vararg bootstrapMethodArguments: Any?
+ name: String?,
+ descriptor: String?,
+ bootstrapMethodHandle: Handle?,
+ vararg bootstrapMethodArguments: Any?
) {
if (emitCode) {
- super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle,
- *bootstrapMethodArguments)
+ super.visitInvokeDynamicInsn(
+ name, descriptor, bootstrapMethodHandle,
+ *bootstrapMethodArguments
+ )
}
}
- final override fun visitJumpInsn(
- opcode: Int,
- label: Label?
- ) {
+ final override fun visitJumpInsn(opcode: Int, label: Label?) {
if (emitCode) {
super.visitJumpInsn(opcode, label)
}
@@ -240,20 +218,17 @@
}
}
- final override fun visitIincInsn(
- varIndex: Int,
- increment: Int
- ) {
+ final override fun visitIincInsn(varIndex: Int, increment: Int) {
if (emitCode) {
super.visitIincInsn(varIndex, increment)
}
}
final override fun visitTableSwitchInsn(
- min: Int,
- max: Int,
- dflt: Label?,
- vararg labels: Label?
+ min: Int,
+ max: Int,
+ dflt: Label?,
+ vararg labels: Label?
) {
if (emitCode) {
super.visitTableSwitchInsn(min, max, dflt, *labels)
@@ -261,29 +236,26 @@
}
final override fun visitLookupSwitchInsn(
- dflt: Label?,
- keys: IntArray?,
- labels: Array<out Label>?
+ dflt: Label?,
+ keys: IntArray?,
+ labels: Array<out Label>?
) {
if (emitCode) {
super.visitLookupSwitchInsn(dflt, keys, labels)
}
}
- final override fun visitMultiANewArrayInsn(
- descriptor: String?,
- numDimensions: Int
- ) {
+ final override fun visitMultiANewArrayInsn(descriptor: String?, numDimensions: Int) {
if (emitCode) {
super.visitMultiANewArrayInsn(descriptor, numDimensions)
}
}
final override fun visitInsnAnnotation(
- typeRef: Int,
- typePath: TypePath?,
- descriptor: String?,
- visible: Boolean
+ typeRef: Int,
+ typePath: TypePath?,
+ descriptor: String?,
+ visible: Boolean
): AnnotationVisitor? {
if (emitCode) {
return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible)
@@ -292,10 +264,10 @@
}
final override fun visitTryCatchBlock(
- start: Label?,
- end: Label?,
- handler: Label?,
- type: String?
+ start: Label?,
+ end: Label?,
+ handler: Label?,
+ type: String?
) {
if (emitCode) {
super.visitTryCatchBlock(start, end, handler, type)
@@ -303,10 +275,10 @@
}
final override fun visitTryCatchAnnotation(
- typeRef: Int,
- typePath: TypePath?,
- descriptor: String?,
- visible: Boolean
+ typeRef: Int,
+ typePath: TypePath?,
+ descriptor: String?,
+ visible: Boolean
): AnnotationVisitor? {
if (emitCode) {
return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible)
@@ -315,12 +287,12 @@
}
final override fun visitLocalVariable(
- name: String?,
- descriptor: String?,
- signature: String?,
- start: Label?,
- end: Label?,
- index: Int
+ name: String?,
+ descriptor: String?,
+ signature: String?,
+ start: Label?,
+ end: Label?,
+ index: Int
) {
if (emitCode) {
super.visitLocalVariable(name, descriptor, signature, start, end, index)
@@ -328,25 +300,23 @@
}
final override fun visitLocalVariableAnnotation(
- typeRef: Int,
- typePath: TypePath?,
- start: Array<out Label>?,
- end: Array<out Label>?,
- index: IntArray?,
- descriptor: String?,
- visible: Boolean
+ typeRef: Int,
+ typePath: TypePath?,
+ start: Array<out Label>?,
+ end: Array<out Label>?,
+ index: IntArray?,
+ descriptor: String?,
+ visible: Boolean
): AnnotationVisitor? {
if (emitCode) {
return super.visitLocalVariableAnnotation(
- typeRef, typePath, start, end, index, descriptor, visible)
+ typeRef, typePath, start, end, index, descriptor, visible
+ )
}
return null
}
- final override fun visitLineNumber(
- line: Int,
- start: Label?
- ) {
+ final override fun visitLineNumber(line: Int, start: Label?) {
if (emitCode) {
super.visitLineNumber(line, start)
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 3d2e142..567a69e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -18,7 +18,6 @@
import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
import com.android.hoststubgen.asm.ClassNodes
-import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
import com.android.hoststubgen.asm.prependArgTypeToMethodDescriptor
import com.android.hoststubgen.asm.writeByteCodeToPushArguments
import com.android.hoststubgen.asm.writeByteCodeToReturn
@@ -42,16 +41,12 @@
* An adapter that generates the "impl" class file from an input class file.
*/
class ImplGeneratingAdapter(
- classes: ClassNodes,
- nextVisitor: ClassVisitor,
- filter: OutputFilter,
- options: Options,
+ classes: ClassNodes,
+ nextVisitor: ClassVisitor,
+ filter: OutputFilter,
+ options: Options,
) : BaseAdapter(classes, nextVisitor, filter, options) {
- override fun shouldEmit(policy: FilterPolicy): Boolean {
- return policy.needsInImpl
- }
-
private var classLoadHooks: List<String> = emptyList()
override fun visit(
@@ -107,14 +102,14 @@
private fun writeClassLoadHookCalls(mv: MethodVisitor) {
classLoadHooks.forEach { classLoadHook ->
// First argument: the class type.
- mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+ mv.visitLdcInsn(Type.getType("L$currentClassName;"))
// Second argument: method name
mv.visitLdcInsn(classLoadHook)
// Call HostTestUtils.onClassLoaded().
mv.visitMethodInsn(
- Opcodes.INVOKESTATIC,
+ INVOKESTATIC,
HostTestUtils.CLASS_INTERNAL_NAME,
"onClassLoaded",
"(Ljava/lang/Class;Ljava/lang/String;)V",
@@ -124,69 +119,49 @@
}
override fun updateAccessFlags(
- access: Int,
- name: String,
- descriptor: String,
+ access: Int,
+ name: String,
+ descriptor: String,
+ policy: FilterPolicy,
): Int {
- if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) {
+ if (policy.isMethodRewriteBody) {
+ // If we are rewriting the entire method body, we need
+ // to convert native methods to non-native
return access and Opcodes.ACC_NATIVE.inv()
}
return access
}
override fun visitMethodInner(
- access: Int,
- name: String,
- descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- policy: FilterPolicyWithReason,
- substituted: Boolean,
- superVisitor: MethodVisitor?,
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ policy: FilterPolicyWithReason,
+ substituted: Boolean,
+ superVisitor: MethodVisitor?,
): MethodVisitor? {
- // Inject method log, if needed.
var innerVisitor = superVisitor
// If method logging is enabled, inject call to the logging method.
val methodCallHooks = filter.getMethodCallHooks(currentClassName, name, descriptor)
if (methodCallHooks.isNotEmpty()) {
innerVisitor = MethodCallHookInjectingAdapter(
- access,
name,
descriptor,
- signature,
- exceptions,
- innerVisitor,
methodCallHooks,
- )
+ innerVisitor,
+ )
}
// If this class already has a class initializer and a class load hook is needed, then
// we inject code.
if (classLoadHooks.isNotEmpty() &&
name == CLASS_INITIALIZER_NAME &&
- descriptor == CLASS_INITIALIZER_DESC) {
- innerVisitor = ClassLoadHookInjectingMethodAdapter(
- access,
- name,
- descriptor,
- signature,
- exceptions,
- innerVisitor,
- )
- }
-
- // If non-stub method call detection is enabled, then inject a call to the checker.
- if (options.enableNonStubMethodCallDetection && doesMethodNeedNonStubCallCheck(
- access, name, descriptor, policy) ) {
- innerVisitor = NonStubMethodCallDetectingAdapter(
- access,
- name,
- descriptor,
- signature,
- exceptions,
- innerVisitor,
- )
+ descriptor == CLASS_INITIALIZER_DESC
+ ) {
+ innerVisitor = ClassLoadHookInjectingMethodAdapter(innerVisitor)
}
fun MethodVisitor.withAnnotation(descriptor: String): MethodVisitor {
@@ -195,34 +170,34 @@
}
log.withIndent {
- var willThrow = false
- if (policy.policy == FilterPolicy.Throw) {
- log.v("Making method throw...")
- willThrow = true
- innerVisitor = ThrowingMethodAdapter(
- access, name, descriptor, signature, exceptions, innerVisitor)
- .withAnnotation(HostStubGenProcessedAsThrow.CLASS_DESCRIPTOR)
+ // When we encounter native methods, we want to forcefully
+ // inject a method body. Also see [updateAccessFlags].
+ val forceCreateBody = (access and Opcodes.ACC_NATIVE) != 0
+ when (policy.policy) {
+ FilterPolicy.Throw -> {
+ log.v("Making method throw...")
+ return ThrowingMethodAdapter(forceCreateBody, innerVisitor)
+ .withAnnotation(HostStubGenProcessedAsThrow.CLASS_DESCRIPTOR)
+ }
+ FilterPolicy.Ignore -> {
+ log.v("Making method ignored...")
+ return IgnoreMethodAdapter(descriptor, forceCreateBody, innerVisitor)
+ .withAnnotation(HostStubGenProcessedAsIgnore.CLASS_DESCRIPTOR)
+ }
+ FilterPolicy.Redirect -> {
+ log.v("Redirecting method...")
+ return RedirectMethodAdapter(
+ access, name, descriptor,
+ forceCreateBody, innerVisitor
+ )
+ .withAnnotation(HostStubGenProcessedAsSubstitute.CLASS_DESCRIPTOR)
+ }
+ else -> {}
}
- if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) {
- log.v("Rewriting native method...")
- return NativeSubstitutingMethodAdapter(
- access, name, descriptor, signature, exceptions, innerVisitor)
- .withAnnotation(HostStubGenProcessedAsSubstitute.CLASS_DESCRIPTOR)
- }
- if (willThrow) {
- return innerVisitor
- }
+ }
- if (policy.policy == FilterPolicy.Ignore) {
- log.v("Making method ignored...")
- return IgnoreMethodAdapter(
- access, name, descriptor, signature, exceptions, innerVisitor)
- .withAnnotation(HostStubGenProcessedAsIgnore.CLASS_DESCRIPTOR)
- }
- if (filter.hasAnyMethodCallReplace()) {
- innerVisitor = MethodCallReplacingAdapter(
- access, name, descriptor, signature, exceptions, innerVisitor)
- }
+ if (filter.hasAnyMethodCallReplace()) {
+ innerVisitor = MethodCallReplacingAdapter(name, innerVisitor)
}
if (substituted) {
innerVisitor?.withAnnotation(HostStubGenProcessedAsSubstitute.CLASS_DESCRIPTOR)
@@ -231,53 +206,32 @@
return innerVisitor
}
- fun doesMethodNeedNonStubCallCheck(
- access: Int,
- name: String,
- descriptor: String,
- policy: FilterPolicyWithReason,
- ): Boolean {
- // If a method is in the stub, then no need to check.
- if (policy.policy.needsInStub) {
- return false
- }
- // If a method is private or package-private, no need to check.
- // Technically test code can use framework package name, so it's a bit too lenient.
- if (isVisibilityPrivateOrPackagePrivate(access)) {
- return false
- }
- // TODO: If the method overrides a method that's accessible by tests, then we shouldn't
- // do the check. (e.g. overrides a stub method or java standard method.)
-
- return true
- }
-
/**
* A method adapter that replaces the method body with a HostTestUtils.onThrowMethodCalled()
* call.
*/
private inner class ThrowingMethodAdapter(
- access: Int,
- val name: String,
- descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- next: MethodVisitor?
- ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) {
+ createBody: Boolean,
+ next: MethodVisitor?
+ ) : BodyReplacingMethodVisitor(createBody, next) {
override fun emitNewCode() {
- visitMethodInsn(Opcodes.INVOKESTATIC,
- HostTestUtils.CLASS_INTERNAL_NAME,
- "onThrowMethodCalled",
- "()V",
- false)
+ visitMethodInsn(
+ INVOKESTATIC,
+ HostTestUtils.CLASS_INTERNAL_NAME,
+ "onThrowMethodCalled",
+ "()V",
+ false
+ )
// We still need a RETURN opcode for the return type.
// For now, let's just inject a `throw`.
visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException")
visitInsn(Opcodes.DUP)
visitLdcInsn("Unreachable")
- visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
- "<init>", "(Ljava/lang/String;)V", false)
+ visitMethodInsn(
+ Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
+ "<init>", "(Ljava/lang/String;)V", false
+ )
visitInsn(Opcodes.ATHROW)
// visitMaxs(3, if (isStatic) 0 else 1)
@@ -289,13 +243,10 @@
* A method adapter that replaces the method body with a no-op return.
*/
private inner class IgnoreMethodAdapter(
- access: Int,
- name: String,
- val descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- next: MethodVisitor?
- ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) {
+ val descriptor: String,
+ createBody: Boolean,
+ next: MethodVisitor?
+ ) : BodyReplacingMethodVisitor(createBody, next) {
override fun emitNewCode() {
when (Type.getReturnType(descriptor)) {
Type.VOID_TYPE -> visitInsn(Opcodes.RETURN)
@@ -326,30 +277,25 @@
}
/**
- * A method adapter that replaces a native method call with a call to the "native substitution"
- * class.
+ * A method adapter that rewrite a method body with a
+ * call to a method in the redirection class.
*/
- private inner class NativeSubstitutingMethodAdapter(
- val access: Int,
- private val name: String,
- private val descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- next: MethodVisitor?
- ) : MethodVisitor(OPCODE_VERSION, next) {
- override fun visitCode() {
- throw RuntimeException("NativeSubstitutingMethodVisitor should be called on " +
- " native method, where visitCode() shouldn't be called.")
- }
+ private inner class RedirectMethodAdapter(
+ access: Int,
+ private val name: String,
+ private val descriptor: String,
+ createBody: Boolean,
+ next: MethodVisitor?
+ ) : BodyReplacingMethodVisitor(createBody, next) {
- override fun visitEnd() {
- super.visitCode()
+ private val isStatic = (access and Opcodes.ACC_STATIC) != 0
+ override fun emitNewCode() {
var targetDescriptor = descriptor
var argOffset = 0
- // For non-static native method, we need to tweak it a bit.
- if ((access and Opcodes.ACC_STATIC) == 0) {
+ // For non-static method, we need to tweak it a bit.
+ if (!isStatic) {
// Push `this` as the first argument.
this.visitVarInsn(Opcodes.ALOAD, 0)
@@ -366,16 +312,17 @@
writeByteCodeToPushArguments(descriptor, this, argOffset)
- visitMethodInsn(Opcodes.INVOKESTATIC,
- nativeSubstitutionClass,
- name,
- targetDescriptor,
- false)
+ visitMethodInsn(
+ INVOKESTATIC,
+ redirectionClass,
+ name,
+ targetDescriptor,
+ false
+ )
writeByteCodeToReturn(descriptor, this)
visitMaxs(99, 0) // We let ASM figure them out.
- super.visitEnd()
}
}
@@ -386,25 +333,22 @@
* `this(...)`. The logging code will be injected *before* such calls.
*/
private inner class MethodCallHookInjectingAdapter(
- access: Int,
- val name: String,
- val descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- next: MethodVisitor?,
- val hooks: List<String>,
+ val name: String,
+ val descriptor: String,
+ val hooks: List<String>,
+ next: MethodVisitor?,
) : MethodVisitor(OPCODE_VERSION, next) {
override fun visitCode() {
super.visitCode()
hooks.forEach { hook ->
- mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+ mv.visitLdcInsn(Type.getType("L$currentClassName;"))
visitLdcInsn(name)
visitLdcInsn(descriptor)
visitLdcInsn(hook)
visitMethodInsn(
- Opcodes.INVOKESTATIC,
+ INVOKESTATIC,
HostTestUtils.CLASS_INTERNAL_NAME,
"callMethodCallHook",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
@@ -418,11 +362,6 @@
* Inject a class load hook call.
*/
private inner class ClassLoadHookInjectingMethodAdapter(
- access: Int,
- val name: String,
- val descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
next: MethodVisitor?
) : MethodVisitor(OPCODE_VERSION, next) {
override fun visitCode() {
@@ -432,53 +371,8 @@
}
}
- /**
- * A method adapter that detects calls to non-stub methods.
- */
- private inner class NonStubMethodCallDetectingAdapter(
- access: Int,
- val name: String,
- val descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- next: MethodVisitor?
- ) : MethodVisitor(OPCODE_VERSION, next) {
- override fun visitCode() {
- super.visitCode()
-
- // First three arguments to HostTestUtils.onNonStubMethodCalled().
- visitLdcInsn(currentClassName)
- visitLdcInsn(name)
- visitLdcInsn(descriptor)
-
- // Call: HostTestUtils.getStackWalker().getCallerClass().
- // This push the caller Class in the stack.
- visitMethodInsn(Opcodes.INVOKESTATIC,
- HostTestUtils.CLASS_INTERNAL_NAME,
- "getStackWalker",
- "()Ljava/lang/StackWalker;",
- false)
- visitMethodInsn(Opcodes.INVOKEVIRTUAL,
- "java/lang/StackWalker",
- "getCallerClass",
- "()Ljava/lang/Class;",
- false)
-
- // Then call onNonStubMethodCalled().
- visitMethodInsn(Opcodes.INVOKESTATIC,
- HostTestUtils.CLASS_INTERNAL_NAME,
- "onNonStubMethodCalled",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V",
- false)
- }
- }
-
private inner class MethodCallReplacingAdapter(
- access: Int,
val callerMethodName: String,
- val descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
next: MethodVisitor?,
) : MethodVisitor(OPCODE_VERSION, next) {
override fun visitMethodInsn(
@@ -497,7 +391,8 @@
}
}
val to = filter.getMethodCallReplaceTo(
- currentClassName, callerMethodName, owner!!, name!!, descriptor!!)
+ currentClassName, callerMethodName, owner!!, name!!, descriptor!!
+ )
if (to == null
// Don't replace if the target is the callsite.
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt
deleted file mode 100644
index fc20f28..0000000
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2023 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.hoststubgen.visitors
-
-import com.android.hoststubgen.asm.ClassNodes
-import com.android.hoststubgen.filters.FilterPolicy
-import com.android.hoststubgen.filters.FilterPolicyWithReason
-import com.android.hoststubgen.filters.OutputFilter
-import com.android.hoststubgen.log
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.MethodVisitor
-import org.objectweb.asm.Opcodes
-
-/**
- * An adapter that generates the "impl" class file from an input class file.
- */
-class StubGeneratingAdapter(
- classes: ClassNodes,
- nextVisitor: ClassVisitor,
- filter: OutputFilter,
- options: Options,
-) : BaseAdapter(classes, nextVisitor, filter, options) {
-
- override fun shouldEmit(policy: FilterPolicy): Boolean {
- return policy.needsInStub
- }
-
- override fun visitMethodInner(
- access: Int,
- name: String,
- descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- policy: FilterPolicyWithReason,
- substituted: Boolean,
- superVisitor: MethodVisitor?,
- ): MethodVisitor? {
- return StubMethodVisitor(access, name, descriptor, signature, exceptions, superVisitor)
- }
-
- private inner class StubMethodVisitor(
- access: Int,
- val name: String,
- descriptor: String,
- signature: String?,
- exceptions: Array<String>?,
- next: MethodVisitor?
- ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) {
- override fun emitNewCode() {
- log.d(" Generating stub method for $currentClassName.$name")
-
- // Inject the following code:
- // throw new RuntimeException("Stub!");
-
- /*
- NEW java/lang/RuntimeException
- DUP
- LDC "not supported on host side"
- INVOKESPECIAL java/lang/RuntimeException.<init> (Ljava/lang/String;)V
- ATHROW
- MAXSTACK = 3
- MAXLOCALS = 2 <- 1 for this, 1 for return value.
- */
- visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException")
- visitInsn(Opcodes.DUP)
- visitLdcInsn("Stub!")
- visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
- "<init>", "(Ljava/lang/String;)V", false)
- visitInsn(Opcodes.ATHROW)
- visitMaxs(0, 0) // We let ASM figure them out.
- }
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
index e7873d6..ba2c869 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -21,7 +21,7 @@
// Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules.
java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host",
+ name: "hoststubgen-test-tiny-framework-host-base",
defaults: ["hoststubgen-command-defaults"],
cmd: hoststubgen_common_options +
"--in-jar $(location :hoststubgen-test-tiny-framework) " +
@@ -35,25 +35,13 @@
}
java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-stub",
+ name: "hoststubgen-test-tiny-framework-host",
cmd: "cp $(in) $(out)",
srcs: [
- ":hoststubgen-test-tiny-framework-host{host_stub.jar}",
+ ":hoststubgen-test-tiny-framework-host-base{host.jar}",
],
out: [
- "host_stub.jar",
- ],
- visibility: ["//visibility:private"],
-}
-
-java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-impl",
- cmd: "cp $(in) $(out)",
- srcs: [
- ":hoststubgen-test-tiny-framework-host{host_impl.jar}",
- ],
- out: [
- "host_impl.jar",
+ "host.jar",
],
visibility: ["//visibility:private"],
}
@@ -61,7 +49,7 @@
// Same as "hoststubgen-test-tiny-framework-host", but with more options, to test more hoststubgen
// features.
java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-ext",
+ name: "hoststubgen-test-tiny-framework-host-ext-base",
defaults: ["hoststubgen-command-defaults"],
cmd: hoststubgen_common_options +
"--in-jar $(location :hoststubgen-test-tiny-framework) " +
@@ -79,37 +67,25 @@
}
java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-ext-stub",
+ name: "hoststubgen-test-tiny-framework-host-ext",
cmd: "cp $(in) $(out)",
srcs: [
- ":hoststubgen-test-tiny-framework-host-ext{host_stub.jar}",
+ ":hoststubgen-test-tiny-framework-host-ext-base{host.jar}",
],
out: [
- "host_stub.jar",
- ],
- visibility: ["//visibility:private"],
-}
-
-java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-ext-impl",
- cmd: "cp $(in) $(out)",
- srcs: [
- ":hoststubgen-test-tiny-framework-host-ext{host_impl.jar}",
- ],
- out: [
- "host_impl.jar",
+ "host.jar",
],
visibility: ["//visibility:private"],
}
// Compile the test jar, using 2 rules.
-// 1. Build the test against the stub.
+// 1. Build the test against the original framework.
java_library_host {
name: "hoststubgen-test-tiny-test-lib",
srcs: ["tiny-test/src/**/*.java"],
libs: [
- "hoststubgen-test-tiny-framework-host-stub",
+ "hoststubgen-test-tiny-framework",
],
static_libs: [
"junit",
@@ -129,7 +105,7 @@
static_libs: [
"hoststubgen-test-tiny-test-lib",
"hoststubgen-helper-runtime",
- "hoststubgen-test-tiny-framework-host-impl",
+ "hoststubgen-test-tiny-framework-host",
],
test_suites: ["general-tests"],
}
@@ -149,49 +125,25 @@
}
java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-stub-dump",
+ name: "hoststubgen-test-tiny-framework-host-dump",
defaults: ["hoststubgen-jar-dump-defaults"],
srcs: [
- ":hoststubgen-test-tiny-framework-host-stub",
+ ":hoststubgen-test-tiny-framework-host",
],
out: [
- "02-hoststubgen-test-tiny-framework-host-stub-dump.txt",
+ "03-hoststubgen-test-tiny-framework-host-dump.txt",
],
visibility: ["//visibility:private"],
}
java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-impl-dump",
+ name: "hoststubgen-test-tiny-framework-host-ext-dump",
defaults: ["hoststubgen-jar-dump-defaults"],
srcs: [
- ":hoststubgen-test-tiny-framework-host-impl",
+ ":hoststubgen-test-tiny-framework-host-ext",
],
out: [
- "03-hoststubgen-test-tiny-framework-host-impl-dump.txt",
- ],
- visibility: ["//visibility:private"],
-}
-
-java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-ext-stub-dump",
- defaults: ["hoststubgen-jar-dump-defaults"],
- srcs: [
- ":hoststubgen-test-tiny-framework-host-ext-stub",
- ],
- out: [
- "12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt",
- ],
- visibility: ["//visibility:private"],
-}
-
-java_genrule_host {
- name: "hoststubgen-test-tiny-framework-host-ext-impl-dump",
- defaults: ["hoststubgen-jar-dump-defaults"],
- srcs: [
- ":hoststubgen-test-tiny-framework-host-ext-impl",
- ],
- out: [
- "13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt",
+ "13-hoststubgen-test-tiny-framework-host-ext-dump.txt",
],
visibility: ["//visibility:private"],
}
@@ -206,11 +158,9 @@
"golden-output/*.txt",
],
java_data: [
- "hoststubgen-test-tiny-framework-host-stub-dump",
- "hoststubgen-test-tiny-framework-host-impl-dump",
"hoststubgen-test-tiny-framework-orig-dump",
- "hoststubgen-test-tiny-framework-host-ext-stub-dump",
- "hoststubgen-test-tiny-framework-host-ext-impl-dump",
+ "hoststubgen-test-tiny-framework-host-dump",
+ "hoststubgen-test-tiny-framework-host-ext-dump",
],
test_suites: ["general-tests"],
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
index bd9e85e..de4cb0c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
@@ -6,10 +6,10 @@
# To allow a specific class to use annotations:
-# com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+# com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
# To disallow a specific class to use annotations:
-# !com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+# !com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
# To allow a specific package to use annotations:
# com.android.hoststubgen.test.*
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index c2f593c..82586bb 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -41,20 +41,40 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
- Compiled from "HostSideTestNativeSubstitutionClass.java"
-public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+## Class: android/hosttest/annotation/HostSideTestRedirect.class
+ Compiled from "HostSideTestRedirect.java"
+public interface android.hosttest.annotation.HostSideTestRedirect extends java.lang.annotation.Annotation
minor version: 0
major version: 61
flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ this_class: #x // android/hosttest/annotation/HostSideTestRedirect
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestRedirect.java"
+RuntimeVisibleAnnotations:
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestRedirectionClass.class
+ Compiled from "HostSideTestRedirectionClass.java"
+public interface android.hosttest.annotation.HostSideTestRedirectionClass extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestRedirectionClass
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
public abstract java.lang.String value();
descriptor: ()Ljava/lang/String;
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
}
-SourceFile: "HostSideTestNativeSubstitutionClass.java"
+SourceFile: "HostSideTestRedirectionClass.java"
RuntimeVisibleAnnotations:
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
@@ -104,26 +124,6 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestStub.class
- Compiled from "HostSideTestStub.java"
-public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
- minor version: 0
- major version: 61
- flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestStub
- super_class: #x // java/lang/Object
- interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestStub.java"
-RuntimeVisibleAnnotations:
- x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
- java.lang.annotation.Target(
- value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
- )
- x: #x(#x=e#x.#x)
- java.lang.annotation.Retention(
- value=Ljava/lang/annotation/RetentionPolicy;.CLASS
- )
## Class: android/hosttest/annotation/HostSideTestSubstitute.class
Compiled from "HostSideTestSubstitute.java"
public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
@@ -187,26 +187,6 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
- Compiled from "HostSideTestWholeClassStub.java"
-public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
- minor version: 0
- major version: 61
- flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub
- super_class: #x // java/lang/Object
- interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestWholeClassStub.java"
-RuntimeVisibleAnnotations:
- x: #x(#x=[e#x.#x])
- java.lang.annotation.Target(
- value=[Ljava/lang/annotation/ElementType;.TYPE]
- )
- x: #x(#x=e#x.#x)
- java.lang.annotation.Retention(
- value=Ljava/lang/annotation/RetentionPolicy;.CLASS
- )
## Class: android/hosttest/annotation/tests/HostSideTestSuppress.class
Compiled from "HostSideTestSuppress.java"
public interface android.hosttest.annotation.tests.HostSideTestSuppress extends java.lang.annotation.Annotation
@@ -394,120 +374,15 @@
com/android/hoststubgen/test/tinyframework/R$Nested
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
- Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
- minor version: 0
- major version: 61
- flags: (0x0020) ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 3
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
- descriptor: ()V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
-
- public static int getOneKeep();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=1, locals=0, args_size=0
- x: iconst_1
- x: ireturn
- LineNumberTable:
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestKeep
-
- public static int getOneStub();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=1, locals=0, args_size=0
- x: iconst_1
- x: ireturn
- LineNumberTable:
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-}
-SourceFile: "TinyFrameworkCallerCheck.java"
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
- Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
+ Compiled from "TinyFrameworkAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 4
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
-
- public static int getOne_withCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=1, locals=0, args_size=0
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- x: ireturn
- LineNumberTable:
-
- public static int getOne_noCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=1, locals=0, args_size=0
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- x: ireturn
- LineNumberTable:
-}
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
- Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 3, methods: 10, attributes: 2
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
+ interfaces: 0, fields: 2, methods: 8, attributes: 2
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -519,7 +394,7 @@
descriptor: I
flags: (0x0001) ACC_PUBLIC
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -528,42 +403,21 @@
x: invokespecial #x // Method java/lang/Object."<init>":()V
x: aload_0
x: iconst_1
- x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
- x: putfield #x // Field keep:I
- x: return
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- x: aload_0
- x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
- 0 6 1 value I
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
x: iload_1
x: iconst_1
x: iadd
@@ -571,7 +425,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
0 4 1 value I
RuntimeInvisibleAnnotations:
x: #x()
@@ -589,7 +443,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
0 8 1 foo Ljava/lang/String;
RuntimeInvisibleAnnotations:
x: #x()
@@ -608,11 +462,9 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
0 10 1 value I
RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
@@ -630,15 +482,13 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
0 4 1 value I
public static native int nativeAddThree(int);
descriptor: (I)I
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
@@ -668,212 +518,19 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestThrow
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
}
-SourceFile: "TinyFrameworkClassAnnotations.java"
+SourceFile: "TinyFrameworkAnnotations.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
- Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 3, methods: 10, attributes: 2
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
-
- public int keep;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
-
- public int remove;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=1, args_size=1
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: aload_0
- x: iconst_1
- x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
- x: putfield #x // Field keep:I
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
- x: aload_0
- x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 6 1 value I
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
- x: iload_1
- x: iconst_1
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 4 1 value I
-
- public void toBeRemoved(java.lang.String);
- descriptor: (Ljava/lang/String;)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- x: athrow
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 8 1 foo Ljava/lang/String;
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String not supported on host side
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 10 1 value I
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestSubstitute(
- suffix="_host"
- )
-
- public int addTwo_host(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
- x: iload_1
- x: iconst_2
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 4 1 value I
-
- public static native int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestSubstitute(
- suffix="_host"
- )
-
- public static int nativeAddThree_host(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=2, locals=1, args_size=1
- x: iload_0
- x: iconst_3
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 4 0 value I
-
- public java.lang.String unsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: ldc #x // String This value shouldn\'t be seen on the host side.
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
@@ -935,7 +592,131 @@
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 6, attributes: 2
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRemove
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ 0 4 1 value I
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ 0 10 1 value I
+ RuntimeInvisibleAnnotations:
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestSubstitute(
+ suffix="_host"
+ )
+
+ public int addTwo_host(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ 0 4 1 value I
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ 0 8 1 foo Ljava/lang/String;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRemove
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestThrow
+}
+SourceFile: "TinyFrameworkClassWideAnnotations.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
Compiled from "TinyFrameworkClassWithInitializerDefault.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
@@ -950,14 +731,14 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.lang.Object sObject;
descriptor: Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault();
descriptor: ()V
@@ -989,7 +770,7 @@
SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
Compiled from "TinyFrameworkClassWithInitializerStub.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
@@ -1004,14 +785,14 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.lang.Object sObject;
descriptor: Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub();
descriptor: ()V
@@ -1047,7 +828,7 @@
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
@@ -1064,21 +845,21 @@
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private final java.lang.String mLongName;
descriptor: Ljava/lang/String;
@@ -1158,7 +939,7 @@
Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.lang.String getLongName();
descriptor: ()Ljava/lang/String;
@@ -1174,7 +955,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.lang.String getShortName();
descriptor: ()Ljava/lang/String;
@@ -1190,7 +971,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1252,7 +1033,7 @@
SourceFile: "TinyFrameworkEnumComplex.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
Compiled from "TinyFrameworkEnumSimple.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
@@ -1267,14 +1048,14 @@
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
@@ -1373,7 +1154,7 @@
SourceFile: "TinyFrameworkEnumSimple.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
@@ -1427,7 +1208,7 @@
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
@@ -1436,15 +1217,11 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
super_class: #x // java/lang/Object
- interfaces: 0, fields: 3, methods: 19, attributes: 1
+ interfaces: 0, fields: 2, methods: 17, attributes: 1
public int stub;
descriptor: I
flags: (0x0001) ACC_PUBLIC
- public int keep;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
-
public int remove;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -1459,35 +1236,17 @@
x: aload_0
x: iconst_1
x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
- x: putfield #x // Field keep:I
- x: return
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- x: aload_0
- x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
- 0 6 1 value I
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
x: iload_1
x: iconst_1
x: iadd
@@ -1699,19 +1458,6 @@
LocalVariableTable:
Start Length Slot Name Signature
0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
}
SourceFile: "TinyFrameworkForTextPolicy.java"
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
@@ -1729,7 +1475,7 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
descriptor: Ljava/util/function/Supplier;
@@ -1737,7 +1483,7 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
descriptor: ()V
@@ -1756,7 +1502,7 @@
0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
@@ -1772,7 +1518,7 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -1785,7 +1531,7 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static java.lang.Integer lambda$getSupplier_static$3();
descriptor: ()Ljava/lang/Integer;
@@ -1840,7 +1586,7 @@
SourceFile: "TinyFrameworkLambdas.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
@@ -1883,7 +1629,7 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
descriptor: Ljava/util/function/Supplier;
@@ -1891,7 +1637,7 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
descriptor: ()V
@@ -1910,7 +1656,7 @@
0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
@@ -1926,7 +1672,7 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -1939,7 +1685,7 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static java.lang.Integer lambda$getSupplier_static$3();
descriptor: ()Ljava/lang/Integer;
@@ -1994,7 +1740,7 @@
SourceFile: "TinyFrameworkLambdas.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
NestMembers:
@@ -2179,7 +1925,7 @@
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
BootstrapMethods:
@@ -2199,7 +1945,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 11, attributes: 2
+ interfaces: 0, fields: 1, methods: 14, attributes: 2
int value;
descriptor: I
flags: (0x0000)
@@ -2220,6 +1966,9 @@
public static native int nativeAddTwo(int);
descriptor: (I)I
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
public static int nativeAddTwo_should_be_like_this(int);
descriptor: (I)I
@@ -2237,6 +1986,9 @@
public static native long nativeLongPlus(long, long);
descriptor: (JJ)J
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
public static long nativeLongPlus_should_be_like_this(long, long);
descriptor: (JJ)J
@@ -2271,6 +2023,9 @@
public native int nativeNonStaticAddToValue(int);
descriptor: (I)I
flags: (0x0101) ACC_PUBLIC, ACC_NATIVE
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
public int nativeNonStaticAddToValue_should_be_like_this(int);
descriptor: (I)I
@@ -2294,6 +2049,10 @@
x: #x()
android.hosttest.annotation.HostSideTestThrow
+ public static native void nativeStillKeep();
+ descriptor: ()V
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
public static void nativeStillNotSupported_should_be_like_this();
descriptor: ()V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -2308,13 +2067,47 @@
public static native byte nativeBytePlus(byte, byte);
descriptor: (BB)B
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
+
+ public void notNativeRedirected();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
+
+ public static void notNativeStaticRedirected();
+ descriptor: ()V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
}
SourceFile: "TinyFrameworkNative.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ android.hosttest.annotation.HostSideTestRedirectionClass(
value="TinyFrameworkNative_host"
)
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
@@ -2325,7 +2118,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 2
+ interfaces: 0, fields: 0, methods: 7, attributes: 2
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2399,6 +2192,25 @@
Start Length Slot Name Signature
0 5 0 arg1 B
0 5 1 arg2 B
+
+ public static void notNativeRedirected(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=0, locals=1, args_size=1
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 1 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+
+ public static void notNativeStaticRedirected();
+ descriptor: ()V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=0, locals=0, args_size=0
+ x: return
+ LineNumberTable:
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeInvisibleAnnotations:
@@ -2684,7 +2496,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 1, attributes: 4
+ interfaces: 0, fields: 2, methods: 1, attributes: 3
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2717,9 +2529,6 @@
<no name> final mandated
}
SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
InnerClasses:
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
@@ -2778,6 +2587,40 @@
InnerClasses:
public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 8
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass;
+}
+SourceFile: "TinyFrameworkNestedClasses.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
Compiled from "TinyFrameworkNestedClasses.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
@@ -2786,7 +2629,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 4
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
@@ -2820,13 +2663,11 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
InnerClasses:
public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
Compiled from "TinyFrameworkNestedClasses.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
@@ -2937,11 +2778,12 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
@@ -2957,6 +2799,7 @@
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
Compiled from "TinyFrameworkPackageRedirect.java"
@@ -2999,7 +2842,7 @@
SourceFile: "TinyFrameworkPackageRedirect.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
Compiled from "TinyFrameworkRenamedClassCaller.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
@@ -3041,7 +2884,7 @@
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
Compiled from "TinyFrameworkToBeRenamed.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
@@ -3088,7 +2931,7 @@
SourceFile: "TinyFrameworkToBeRenamed.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
Compiled from "A.java"
public class com.android.hoststubgen.test.tinyframework.packagetest.A
@@ -3834,4 +3677,4 @@
SourceFile: "UnsupportedClass.java"
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
deleted file mode 100644
index 1b83d24..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ /dev/null
@@ -1,2829 +0,0 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
- Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int addTwo(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
- Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int addOne(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
- Compiled from "IPretendingAidl.java"
-public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 4
-}
-InnerClasses:
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
- Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R$Nested
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 4
- public static int[] ARRAY;
- descriptor: [I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.R$Nested();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/R
-## Class: com/android/hoststubgen/test/tinyframework/R.class
- Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/R
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 1, attributes: 4
- public com.android.hoststubgen.test.tinyframework.R();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
- com/android/hoststubgen/test/tinyframework/R$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
- Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
- minor version: 0
- major version: 61
- flags: (0x0020) ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 4
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
- descriptor: ()V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOneStub();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-}
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
- Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 5
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOne_withCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOne_noCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
- Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 5, attributes: 3
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-}
-SourceFile: "TinyFrameworkClassAnnotations.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestClassLoadHook(
- value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
- )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
- Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 3, methods: 8, attributes: 3
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int keep;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int remove;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public void toBeRemoved(java.lang.String);
- descriptor: (Ljava/lang/String;)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String unsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
- Compiled from "TinyFrameworkClassLoadHook.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 3, attributes: 3
- public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
- descriptor: Ljava/util/Set;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
- descriptor: ()V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static void onClassLoaded(java.lang.Class<?>);
- descriptor: (Ljava/lang/Class;)V
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // (Ljava/lang/Class<*>;)V
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassLoadHook.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
- Compiled from "TinyFrameworkClassWithInitializerDefault.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 0, attributes: 3
- public static boolean sInitialized;
- descriptor: Z
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static java.lang.Object sObject;
- descriptor: Ljava/lang/Object;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
- Compiled from "TinyFrameworkClassWithInitializerStub.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 0, attributes: 3
- public static boolean sInitialized;
- descriptor: Z
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static java.lang.Object sObject;
- descriptor: Ljava/lang/Object;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerStub.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestClassLoadHook(
- value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
- )
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerKeep
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
- Compiled from "TinyFrameworkEnumComplex.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
- minor version: 0
- major version: 61
- flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
- super_class: #x // java/lang/Enum
- interfaces: 0, fields: 4, methods: 7, attributes: 4
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
- descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
- descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
- descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- MethodParameters:
- Name Flags
- <no name> mandated
-
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
- descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=3, locals=5, args_size=5
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- MethodParameters:
- Name Flags
- <no name> synthetic
- <no name> synthetic
- <no name>
- <no name>
-
- public java.lang.String getLongName();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public java.lang.String getShortName();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
- descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
-SourceFile: "TinyFrameworkEnumComplex.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
- Compiled from "TinyFrameworkEnumSimple.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
- minor version: 0
- major version: 61
- flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
- super_class: #x // java/lang/Enum
- interfaces: 0, fields: 3, methods: 5, attributes: 4
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
- descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
- descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
- descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- MethodParameters:
- Name Flags
- <no name> mandated
-
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
- descriptor: (Ljava/lang/String;I)V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=3, locals=3, args_size=3
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()V
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- MethodParameters:
- Name Flags
- <no name> synthetic
- <no name> synthetic
-
- private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
- descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
-SourceFile: "TinyFrameworkEnumSimple.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
- Compiled from "TinyFrameworkExceptionTester.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int testException();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkExceptionTester.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
- Compiled from "TinyFrameworkForTextPolicy.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 14, attributes: 2
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String toBeIgnoredObj();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public void toBeIgnoredV();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public boolean toBeIgnoredZ();
- descriptor: ()Z
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public byte toBeIgnoredB();
- descriptor: ()B
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public char toBeIgnoredC();
- descriptor: ()C
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public short toBeIgnoredS();
- descriptor: ()S
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int toBeIgnoredI();
- descriptor: ()I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public float toBeIgnoredF();
- descriptor: ()F
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public double toBeIgnoredD();
- descriptor: ()D
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkForTextPolicy.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
- Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 7, attributes: 5
- public final java.util.function.Supplier<java.lang.Integer> mSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0011) ACC_PUBLIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public java.util.function.Supplier<java.lang.Integer> getSupplier();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static java.lang.Integer lambda$getSupplier_static$3();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$getSupplier$2();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$static$1();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$new$0();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerKeep
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
- Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 7, attributes: 5
- public final java.util.function.Supplier<java.lang.Integer> mSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0011) ACC_PUBLIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public java.util.function.Supplier<java.lang.Integer> getSupplier();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static java.lang.Integer lambda$getSupplier_static$3();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$getSupplier$2();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$static$1();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$new$0();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerKeep
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
- Compiled from "TinyFrameworkMethodCallReplace.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 4
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static void startThread(java.lang.Thread);
- descriptor: (Ljava/lang/Thread;)V
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int add(int, int);
- descriptor: (II)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
-SourceFile: "TinyFrameworkMethodCallReplace.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
- Compiled from "TinyFrameworkMethodCallReplace.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 5
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception;
- descriptor: ()Z
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Exceptions:
- throws java.lang.Exception
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int staticMethodCallReplaceTester();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
- descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkMethodCallReplace.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
- Compiled from "TinyFrameworkNative.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 10, attributes: 3
- int value;
- descriptor: I
- flags: (0x0000)
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static native int nativeAddTwo(int);
- descriptor: (I)I
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddTwo_should_be_like_this(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static native long nativeLongPlus(long, long);
- descriptor: (JJ)J
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static long nativeLongPlus_should_be_like_this(long, long);
- descriptor: (JJ)J
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=4, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public void setValue(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public native int nativeNonStaticAddToValue(int);
- descriptor: (I)I
- flags: (0x0101) ACC_PUBLIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int nativeNonStaticAddToValue_should_be_like_this(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static void nativeStillNotSupported_should_be_like_this();
- descriptor: ()V
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static native byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkNative.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
- x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
- value="TinyFrameworkNative_host"
- )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 1, attributes: 4
- public int value;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 1, attributes: 5
- public int value;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
- descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- MethodParameters:
- Name Flags
- <no name> final mandated
-}
-InnerClasses:
- public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 5
- public int value;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
- super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
- interfaces: 0, fields: 0, methods: 1, attributes: 4
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 4, attributes: 5
- public final java.util.function.Supplier<java.lang.Integer> mSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0011) ACC_PUBLIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.util.function.Supplier<java.lang.Integer> getSupplier();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
- Compiled from "TinyFrameworkPackageRedirect.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int foo(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkPackageRedirect.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
- Compiled from "TinyFrameworkRenamedClassCaller.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int foo(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkRenamedClassCaller.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
- Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.A
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
- Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
- Compiled from "C1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C1.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
- Compiled from "C2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
- super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C2.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
- Compiled from "C3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
- super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C3.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
- Compiled from "CA.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CA.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
- Compiled from "CB.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CB.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
- Compiled from "I1.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I1.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
- Compiled from "I2.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
- super_class: #x // java/lang/Object
- interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I2.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
- Compiled from "I3.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
- super_class: #x // java/lang/Object
- interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I3.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
- Compiled from "IA.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IA.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
- Compiled from "IB.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IB.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/unsupported/UnsupportedClass.class
- Compiled from "UnsupportedClass.java"
-public class com.unsupported.UnsupportedClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/unsupported/UnsupportedClass
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.unsupported.UnsupportedClass(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int getValue();
- descriptor: ()I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "UnsupportedClass.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
- Compiled from "TinyFrameworkToBeRenamed.java"
-public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 3
- private final int mValue;
- descriptor: I
- flags: (0x0012) ACC_PRIVATE, ACC_FINAL
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int getValue();
- descriptor: ()I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkToBeRenamed.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
similarity index 68%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
rename to tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
index d23b450..31bbcc5 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -12,12 +12,12 @@
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "HostSideTestClassLoadHook.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
@@ -39,7 +39,7 @@
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
@@ -48,13 +48,35 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
- Compiled from "HostSideTestNativeSubstitutionClass.java"
-public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+## Class: android/hosttest/annotation/HostSideTestRedirect.class
+ Compiled from "HostSideTestRedirect.java"
+public interface android.hosttest.annotation.HostSideTestRedirect extends java.lang.annotation.Annotation
minor version: 0
major version: 61
flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ this_class: #x // android/hosttest/annotation/HostSideTestRedirect
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestRedirect.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestRedirectionClass.class
+ Compiled from "HostSideTestRedirectionClass.java"
+public interface android.hosttest.annotation.HostSideTestRedirectionClass extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestRedirectionClass
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
public abstract java.lang.String value();
@@ -62,12 +84,12 @@
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
-SourceFile: "HostSideTestNativeSubstitutionClass.java"
+SourceFile: "HostSideTestRedirectionClass.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
@@ -89,7 +111,7 @@
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
@@ -98,20 +120,20 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestStub.class
- Compiled from "HostSideTestStub.java"
-public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
+## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class
+ Compiled from "HostSideTestStaticInitializerKeep.java"
+public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation
minor version: 0
major version: 61
flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestStub
+ this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
}
-SourceFile: "HostSideTestStub.java"
+SourceFile: "HostSideTestStaticInitializerKeep.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
@@ -134,12 +156,12 @@
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "HostSideTestSubstitute.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD]
@@ -161,7 +183,7 @@
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
@@ -183,29 +205,7 @@
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- x: #x(#x=[e#x.#x])
- java.lang.annotation.Target(
- value=[Ljava/lang/annotation/ElementType;.TYPE]
- )
- x: #x(#x=e#x.#x)
- java.lang.annotation.Retention(
- value=Ljava/lang/annotation/RetentionPolicy;.CLASS
- )
-## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
- Compiled from "HostSideTestWholeClassStub.java"
-public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
- minor version: 0
- major version: 61
- flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub
- super_class: #x // java/lang/Object
- interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestWholeClassStub.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
@@ -237,9 +237,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int addTwo(int);
descriptor: (I)I
@@ -256,9 +254,7 @@
0 4 0 a I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
@@ -266,9 +262,7 @@
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
Compiled from "IPretendingAidl.java"
@@ -293,9 +287,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int addOne(int);
descriptor: (I)I
@@ -312,19 +304,15 @@
0 4 0 a I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
Compiled from "IPretendingAidl.java"
@@ -342,9 +330,7 @@
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
@@ -362,9 +348,7 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.android.hoststubgen.test.tinyframework.R$Nested();
descriptor: ()V
@@ -380,9 +364,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -400,18 +382,14 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
SourceFile: "R.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/R
## Class: com/android/hoststubgen/test/tinyframework/R.class
Compiled from "R.java"
@@ -436,189 +414,31 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
SourceFile: "R.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/R$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
- Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
- minor version: 0
- major version: 61
- flags: (0x0020) ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 4
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
- descriptor: ()V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOneKeep();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=0, args_size=0
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- x: ldc #x // String getOneKeep
- x: ldc #x // String ()I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iconst_1
- x: ireturn
- LineNumberTable:
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestKeep
-
- public static int getOneStub();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=1, locals=0, args_size=0
- x: iconst_1
- x: ireturn
- LineNumberTable:
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-}
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
- Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
+ Compiled from "TinyFrameworkAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 5
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOne_withCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=1, locals=0, args_size=0
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- x: ireturn
- LineNumberTable:
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOne_noCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=1, locals=0, args_size=0
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- x: ireturn
- LineNumberTable:
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
- Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 8, attributes: 3
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
+ interfaces: 0, fields: 1, methods: 6, attributes: 3
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestKeep
@@ -628,12 +448,12 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -642,70 +462,36 @@
x: invokespecial #x // Method java/lang/Object."<init>":()V
x: aload_0
x: iconst_1
- x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
- x: putfield #x // Field keep:I
- x: return
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- x: aload_0
x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
+ x: iconst_1
+ x: iadd
x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
- 0 6 1 value I
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+ 0 4 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- x: ldc #x // String addOneInner
- x: ldc #x // String (I)I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iload_1
- x: iconst_1
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
- 15 4 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestKeep
@@ -722,15 +508,13 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
0 4 1 value I
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeAddThree(int);
descriptor: (I)I
@@ -749,277 +533,39 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.String unsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- x: ldc #x // String unsupportedMethod
- x: ldc #x // String ()Ljava/lang/String;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Unreachable
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ stack=3, locals=1, args_size=1
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestThrow
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
}
-SourceFile: "TinyFrameworkClassAnnotations.java"
+SourceFile: "TinyFrameworkAnnotations.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
- Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 3, methods: 8, attributes: 3
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int keep;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int remove;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=1, args_size=1
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: aload_0
- x: iconst_1
- x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
- x: putfield #x // Field keep:I
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
- x: aload_0
- x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 6 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
- x: iload_1
- x: iconst_1
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 4 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public void toBeRemoved(java.lang.String);
- descriptor: (Ljava/lang/String;)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- x: athrow
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 8 1 foo Ljava/lang/String;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=2, locals=2, args_size=2
- x: iload_1
- x: iconst_2
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 0 4 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=2, locals=1, args_size=1
- x: iload_0
- x: iconst_3
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 4 0 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String unsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: ldc #x // String This value shouldn\'t be seen on the host side.
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
@@ -1035,9 +581,7 @@
Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
descriptor: ()V
@@ -1053,9 +597,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static void onClassLoaded(java.lang.Class<?>);
descriptor: (Ljava/lang/Class;)V
@@ -1077,9 +619,7 @@
Signature: #x // (Ljava/lang/Class<*>;)V
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -1094,19 +634,115 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 4, attributes: 3
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ 0 4 1 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ 0 4 1 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestThrow
+}
+SourceFile: "TinyFrameworkClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
Compiled from "TinyFrameworkClassWithInitializerDefault.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
@@ -1121,35 +757,29 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.lang.Object sObject;
descriptor: Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
}
SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
Compiled from "TinyFrameworkClassWithInitializerStub.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
@@ -1164,24 +794,20 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.lang.Object sObject;
descriptor: Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
static {};
descriptor: ()V
@@ -1201,21 +827,19 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkClassWithInitializerStub.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
@@ -1232,43 +856,37 @@
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private final java.lang.String mLongName;
descriptor: Ljava/lang/String;
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestKeep
@@ -1278,7 +896,7 @@
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestKeep
@@ -1288,9 +906,7 @@
flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1304,9 +920,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1324,9 +938,7 @@
0 10 0 name Ljava/lang/String;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> mandated
@@ -1356,12 +968,10 @@
Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
MethodParameters:
Name Flags
<no name> synthetic
@@ -1383,12 +993,10 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.lang.String getShortName();
descriptor: ()Ljava/lang/String;
@@ -1404,12 +1012,10 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1434,9 +1040,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -1473,20 +1077,16 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
SourceFile: "TinyFrameworkEnumComplex.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
Compiled from "TinyFrameworkEnumSimple.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
@@ -1501,33 +1101,27 @@
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
@@ -1541,9 +1135,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
@@ -1561,9 +1153,7 @@
0 10 0 name Ljava/lang/String;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> mandated
@@ -1585,9 +1175,7 @@
Signature: #x // ()V
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> synthetic
@@ -1612,9 +1200,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -1639,20 +1225,16 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
SourceFile: "TinyFrameworkEnumSimple.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
@@ -1676,9 +1258,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int testException();
descriptor: ()I
@@ -1709,19 +1289,15 @@
11 11 0 e Ljava/lang/Exception;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
@@ -1730,22 +1306,13 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 17, attributes: 2
+ interfaces: 0, fields: 1, methods: 15, attributes: 2
public int stub;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int keep;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -1753,7 +1320,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -1767,63 +1334,32 @@
x: aload_0
x: iconst_1
x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
- x: putfield #x // Field keep:I
- x: return
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- x: aload_0
x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
+ x: iconst_1
+ x: iadd
x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
- 0 6 1 value I
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 4 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String addOneInner
- x: ldc #x // String (I)I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iload_1
- x: iconst_1
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
- 15 4 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.String toBeIgnoredObj();
descriptor: ()Ljava/lang/String;
@@ -1836,9 +1372,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public void toBeIgnoredV();
descriptor: ()V
@@ -1850,9 +1384,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public boolean toBeIgnoredZ();
descriptor: ()Z
@@ -1865,9 +1397,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public byte toBeIgnoredB();
descriptor: ()B
@@ -1880,9 +1410,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public char toBeIgnoredC();
descriptor: ()C
@@ -1895,9 +1423,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public short toBeIgnoredS();
descriptor: ()S
@@ -1910,9 +1436,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int toBeIgnoredI();
descriptor: ()I
@@ -1925,9 +1449,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public float toBeIgnoredF();
descriptor: ()F
@@ -1940,9 +1462,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public double toBeIgnoredD();
descriptor: ()D
@@ -1955,9 +1475,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int addTwo(int);
descriptor: (I)I
@@ -1977,9 +1495,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeAddThree(int);
descriptor: (I)I
@@ -1998,57 +1514,29 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.String unsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String unsupportedMethod
- x: ldc #x // String ()Ljava/lang/String;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Unreachable
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ stack=3, locals=1, args_size=1
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=1, args_size=1
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkForTextPolicy.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
Compiled from "TinyFrameworkLambdas.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
@@ -2064,12 +1552,10 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
descriptor: Ljava/util/function/Supplier;
@@ -2077,12 +1563,10 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
descriptor: ()V
@@ -2101,12 +1585,10 @@
0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
@@ -2122,12 +1604,10 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -2140,12 +1620,10 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static java.lang.Integer lambda$getSupplier_static$3();
descriptor: ()Ljava/lang/Integer;
@@ -2158,9 +1636,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$getSupplier$2();
descriptor: ()Ljava/lang/Integer;
@@ -2173,9 +1649,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$static$1();
descriptor: ()Ljava/lang/Integer;
@@ -2188,9 +1662,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$new$0();
descriptor: ()Ljava/lang/Integer;
@@ -2203,9 +1675,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -2218,7 +1688,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
@@ -2226,12 +1696,10 @@
SourceFile: "TinyFrameworkLambdas.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
BootstrapMethods:
@@ -2271,12 +1739,10 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
descriptor: Ljava/util/function/Supplier;
@@ -2284,12 +1750,10 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
descriptor: ()V
@@ -2308,12 +1772,10 @@
0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
@@ -2329,12 +1791,10 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -2347,12 +1807,10 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static java.lang.Integer lambda$getSupplier_static$3();
descriptor: ()Ljava/lang/Integer;
@@ -2365,9 +1823,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$getSupplier$2();
descriptor: ()Ljava/lang/Integer;
@@ -2380,9 +1836,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$static$1();
descriptor: ()Ljava/lang/Integer;
@@ -2395,9 +1849,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$new$0();
descriptor: ()Ljava/lang/Integer;
@@ -2410,9 +1862,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -2425,7 +1875,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
@@ -2433,12 +1883,10 @@
SourceFile: "TinyFrameworkLambdas.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
BootstrapMethods:
@@ -2487,9 +1935,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static void startThread(java.lang.Thread);
descriptor: (Ljava/lang/Thread;)V
@@ -2508,9 +1954,7 @@
0 10 0 thread Ljava/lang/Thread;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int add(int, int);
descriptor: (II)I
@@ -2528,18 +1972,14 @@
0 4 1 b I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
Compiled from "TinyFrameworkMethodCallReplace.java"
@@ -2564,9 +2004,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception;
descriptor: ()Z
@@ -2600,9 +2038,7 @@
throws java.lang.Exception
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int staticMethodCallReplaceTester();
descriptor: ()I
@@ -2616,9 +2052,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
@@ -2636,22 +2070,18 @@
0 11 0 ab Ljava/util/concurrent/atomic/AtomicBoolean;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
BootstrapMethods:
x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
@@ -2668,15 +2098,13 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 11, attributes: 3
+ interfaces: 0, fields: 1, methods: 14, attributes: 3
int value;
descriptor: I
flags: (0x0000)
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
descriptor: ()V
@@ -2692,9 +2120,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeAddTwo(int);
descriptor: (I)I
@@ -2708,9 +2134,10 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ android.hosttest.annotation.HostSideTestRedirect
public static int nativeAddTwo_should_be_like_this(int);
descriptor: (I)I
@@ -2726,9 +2153,7 @@
0 5 0 arg I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static long nativeLongPlus(long, long);
descriptor: (JJ)J
@@ -2743,9 +2168,10 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ android.hosttest.annotation.HostSideTestRedirect
public static long nativeLongPlus_should_be_like_this(long, long);
descriptor: (JJ)J
@@ -2763,9 +2189,7 @@
0 6 2 arg2 J
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public void setValue(int);
descriptor: (I)V
@@ -2783,9 +2207,7 @@
0 6 1 v I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int nativeNonStaticAddToValue(int);
descriptor: (I)I
@@ -2800,9 +2222,10 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ android.hosttest.annotation.HostSideTestRedirect
public int nativeNonStaticAddToValue_should_be_like_this(int);
descriptor: (I)I
@@ -2820,38 +2243,35 @@
0 6 1 arg I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static void nativeStillNotSupported();
descriptor: ()V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
- stack=4, locals=0, args_size=0
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
- x: ldc #x // String nativeStillNotSupported
- x: ldc #x // String ()V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Unreachable
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ stack=3, locals=0, args_size=0
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestThrow
+ public static native void nativeStillKeep();
+ descriptor: ()V
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
public static void nativeStillNotSupported_should_be_like_this();
descriptor: ()V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -2864,9 +2284,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static byte nativeBytePlus(byte, byte);
descriptor: (BB)B
@@ -2881,21 +2299,53 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ android.hosttest.annotation.HostSideTestRedirect
+
+ public void notNativeRedirected();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.notNativeRedirected:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V
+ x: return
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
+
+ public static void notNativeStaticRedirected();
+ descriptor: ()V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=0, locals=0, args_size=0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.notNativeStaticRedirected:()V
+ x: return
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ android.hosttest.annotation.HostSideTestRedirectionClass(
value="TinyFrameworkNative_host"
)
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
@@ -2906,130 +2356,125 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 3
+ interfaces: 0, fields: 0, methods: 7, attributes: 3
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String <init>
- x: ldc #x // String ()V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: return
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeAddTwo(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeAddTwo
- x: ldc #x // String (I)I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iload_0
- x: iconst_2
- x: iadd
- x: ireturn
+ stack=2, locals=1, args_size=1
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 4 0 arg I
+ 0 4 0 arg I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static long nativeLongPlus(long, long);
descriptor: (JJ)J
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeLongPlus
- x: ldc #x // String (JJ)J
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: lload_0
- x: lload_2
- x: ladd
- x: lreturn
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 4 0 arg1 J
- 15 4 2 arg2 J
+ 0 4 0 arg1 J
+ 0 4 2 arg2 J
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeNonStaticAddToValue
- x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
- x: iload_1
- x: iadd
- x: ireturn
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
+ x: iload_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
- 15 7 1 arg I
+ 0 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 0 7 1 arg I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static byte nativeBytePlus(byte, byte);
descriptor: (BB)B
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeBytePlus
- x: ldc #x // String (BB)B
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iload_0
- x: iload_1
- x: iadd
- x: i2b
- x: ireturn
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: i2b
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 arg1 B
- 15 5 1 arg2 B
+ 0 5 0 arg1 B
+ 0 5 1 arg2 B
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static void notNativeRedirected(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=0, locals=1, args_size=1
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 1 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static void notNativeStaticRedirected();
+ descriptor: ()V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=0, locals=0, args_size=0
+ x: return
+ LineNumberTable:
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
@@ -3047,7 +2492,7 @@
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
@@ -3067,7 +2512,7 @@
0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> final mandated
@@ -3076,45 +2521,33 @@
descriptor: ()Ljava/lang/Integer;
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iconst_1
- x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: invokevirtual #x // Method get:()Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
@@ -3123,7 +2556,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3148,51 +2581,39 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Integer get();
descriptor: ()Ljava/lang/Integer;
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iconst_2
- x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: invokevirtual #x // Method get:()Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
@@ -3201,7 +2622,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3217,7 +2638,7 @@
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
@@ -3237,7 +2658,7 @@
0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> final mandated
@@ -3246,45 +2667,33 @@
descriptor: ()Ljava/lang/Integer;
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iconst_3
- x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: invokevirtual #x // Method get:()Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
@@ -3293,7 +2702,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3318,51 +2727,39 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Integer get();
descriptor: ()Ljava/lang/Integer;
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iconst_4
- x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: invokevirtual #x // Method get:()Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
@@ -3371,7 +2768,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3387,9 +2784,7 @@
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
descriptor: (I)V
@@ -3409,18 +2804,14 @@
0 10 1 x I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3430,24 +2821,20 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 1, attributes: 5
+ interfaces: 0, fields: 2, methods: 1, attributes: 4
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
@@ -3470,24 +2857,17 @@
0 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> final mandated
}
InnerClasses:
- public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3512,51 +2892,39 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Integer get();
descriptor: ()Ljava/lang/Integer;
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: bipush 7
- x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: invokevirtual #x // Method get:()Ljava/lang/Integer;
- x: areturn
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
@@ -3566,7 +2934,50 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 8
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3576,15 +2987,13 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 5
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
descriptor: ()V
@@ -3603,9 +3012,7 @@
0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -3620,22 +3027,16 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3662,19 +3063,15 @@
0 6 1 x I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3691,9 +3088,7 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
descriptor: Ljava/util/function/Supplier;
@@ -3701,9 +3096,7 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
descriptor: ()V
@@ -3725,9 +3118,7 @@
0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
@@ -3746,9 +3137,7 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -3763,9 +3152,7 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -3780,33 +3167,31 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
@@ -3836,9 +3221,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int foo(int);
descriptor: (I)I
@@ -3857,19 +3240,15 @@
0 12 0 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkPackageRedirect.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
Compiled from "TinyFrameworkRenamedClassCaller.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
@@ -3893,9 +3272,7 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int foo(int);
descriptor: (I)I
@@ -3914,19 +3291,15 @@
0 12 0 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
Compiled from "A.java"
public class com.android.hoststubgen.test.tinyframework.packagetest.A
@@ -3940,9 +3313,7 @@
SourceFile: "A.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
Compiled from "A.java"
public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
@@ -3956,9 +3327,7 @@
SourceFile: "A.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
Compiled from "C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -3972,9 +3341,7 @@
SourceFile: "C1.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
Compiled from "C2.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -3988,9 +3355,7 @@
SourceFile: "C2.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
Compiled from "C3.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
@@ -4004,9 +3369,7 @@
SourceFile: "C3.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
Compiled from "CA.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
@@ -4020,9 +3383,7 @@
SourceFile: "CA.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
Compiled from "CB.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
@@ -4036,9 +3397,7 @@
SourceFile: "CB.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
Compiled from "Class_C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -4052,7 +3411,7 @@
SourceFile: "Class_C1.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
Compiled from "Class_C2.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
@@ -4066,7 +3425,7 @@
SourceFile: "Class_C2.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
Compiled from "Class_C3.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
@@ -4080,7 +3439,7 @@
SourceFile: "Class_C3.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
Compiled from "Class_I1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
@@ -4094,7 +3453,7 @@
SourceFile: "Class_I1.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
Compiled from "Class_I1_IA.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
@@ -4108,7 +3467,7 @@
SourceFile: "Class_I1_IA.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
Compiled from "Class_I2.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
@@ -4122,7 +3481,7 @@
SourceFile: "Class_I2.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
Compiled from "Class_I3.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
@@ -4136,7 +3495,7 @@
SourceFile: "Class_I3.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
Compiled from "I1.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
@@ -4150,9 +3509,7 @@
SourceFile: "I1.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
Compiled from "I2.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
@@ -4166,9 +3523,7 @@
SourceFile: "I2.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
Compiled from "I3.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
@@ -4182,9 +3537,7 @@
SourceFile: "I3.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
Compiled from "IA.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
@@ -4198,9 +3551,7 @@
SourceFile: "IA.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
Compiled from "IB.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
@@ -4214,9 +3565,7 @@
SourceFile: "IB.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/supported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.supported.UnsupportedClass
@@ -4231,60 +3580,48 @@
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.supported.UnsupportedClass(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // String com/supported/UnsupportedClass
- x: ldc #x // String <init>
- x: ldc #x // String (I)V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: aload_0
- x: iload_1
- x: putfield #x // Field mValue:I
- x: return
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field mValue:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 10 0 this Lcom/supported/UnsupportedClass;
- 15 10 1 value I
+ 0 10 0 this Lcom/supported/UnsupportedClass;
+ 0 10 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int getValue();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // String com/supported/UnsupportedClass
- x: ldc #x // String getValue
- x: ldc #x // String ()I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: aload_0
- x: getfield #x // Field mValue:I
- x: ireturn
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: getfield #x // Field mValue:I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 15 5 0 this Lcom/supported/UnsupportedClass;
+ 0 5 0 this Lcom/supported/UnsupportedClass;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "UnsupportedClass.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
@@ -4316,9 +3653,7 @@
0 14 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int getValue();
descriptor: ()I
@@ -4336,19 +3671,15 @@
0 10 0 this Lcom/unsupported/UnsupportedClass;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "UnsupportedClass.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
Compiled from "TinyFrameworkToBeRenamed.java"
public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
@@ -4363,9 +3694,7 @@
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
descriptor: (I)V
@@ -4385,9 +3714,7 @@
0 10 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int getValue();
descriptor: ()I
@@ -4403,16 +3730,12 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkToBeRenamed.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
deleted file mode 100644
index 1b83d24..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ /dev/null
@@ -1,2829 +0,0 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
- Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int addTwo(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
- Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int addOne(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
- Compiled from "IPretendingAidl.java"
-public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 4
-}
-InnerClasses:
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
- Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R$Nested
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 4
- public static int[] ARRAY;
- descriptor: [I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.R$Nested();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/R
-## Class: com/android/hoststubgen/test/tinyframework/R.class
- Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/R
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 1, attributes: 4
- public com.android.hoststubgen.test.tinyframework.R();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
- com/android/hoststubgen/test/tinyframework/R$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
- Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
- minor version: 0
- major version: 61
- flags: (0x0020) ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 4
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
- descriptor: ()V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOneStub();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-}
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
- Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 5
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOne_withCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOne_noCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
- Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 5, attributes: 3
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-}
-SourceFile: "TinyFrameworkClassAnnotations.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestClassLoadHook(
- value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
- )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
- Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 3, methods: 8, attributes: 3
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int keep;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int remove;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public void toBeRemoved(java.lang.String);
- descriptor: (Ljava/lang/String;)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String unsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
- Compiled from "TinyFrameworkClassLoadHook.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 3, attributes: 3
- public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
- descriptor: Ljava/util/Set;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
- descriptor: ()V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static void onClassLoaded(java.lang.Class<?>);
- descriptor: (Ljava/lang/Class;)V
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // (Ljava/lang/Class<*>;)V
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassLoadHook.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
- Compiled from "TinyFrameworkClassWithInitializerDefault.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 0, attributes: 3
- public static boolean sInitialized;
- descriptor: Z
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static java.lang.Object sObject;
- descriptor: Ljava/lang/Object;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
- Compiled from "TinyFrameworkClassWithInitializerStub.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 0, attributes: 3
- public static boolean sInitialized;
- descriptor: Z
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static java.lang.Object sObject;
- descriptor: Ljava/lang/Object;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerStub.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestClassLoadHook(
- value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
- )
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerKeep
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
- Compiled from "TinyFrameworkEnumComplex.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
- minor version: 0
- major version: 61
- flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
- super_class: #x // java/lang/Enum
- interfaces: 0, fields: 4, methods: 7, attributes: 4
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
- descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
- descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
- descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- MethodParameters:
- Name Flags
- <no name> mandated
-
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
- descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=3, locals=5, args_size=5
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- MethodParameters:
- Name Flags
- <no name> synthetic
- <no name> synthetic
- <no name>
- <no name>
-
- public java.lang.String getLongName();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public java.lang.String getShortName();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
- descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
-SourceFile: "TinyFrameworkEnumComplex.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
- Compiled from "TinyFrameworkEnumSimple.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
- minor version: 0
- major version: 61
- flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
- super_class: #x // java/lang/Enum
- interfaces: 0, fields: 3, methods: 5, attributes: 4
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
- descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
- descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
- descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- MethodParameters:
- Name Flags
- <no name> mandated
-
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
- descriptor: (Ljava/lang/String;I)V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=3, locals=3, args_size=3
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()V
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- MethodParameters:
- Name Flags
- <no name> synthetic
- <no name> synthetic
-
- private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
- descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
-SourceFile: "TinyFrameworkEnumSimple.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
- Compiled from "TinyFrameworkExceptionTester.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int testException();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkExceptionTester.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
- Compiled from "TinyFrameworkForTextPolicy.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 14, attributes: 2
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String toBeIgnoredObj();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public void toBeIgnoredV();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public boolean toBeIgnoredZ();
- descriptor: ()Z
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public byte toBeIgnoredB();
- descriptor: ()B
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public char toBeIgnoredC();
- descriptor: ()C
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public short toBeIgnoredS();
- descriptor: ()S
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int toBeIgnoredI();
- descriptor: ()I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public float toBeIgnoredF();
- descriptor: ()F
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public double toBeIgnoredD();
- descriptor: ()D
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkForTextPolicy.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
- Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 7, attributes: 5
- public final java.util.function.Supplier<java.lang.Integer> mSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0011) ACC_PUBLIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public java.util.function.Supplier<java.lang.Integer> getSupplier();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static java.lang.Integer lambda$getSupplier_static$3();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$getSupplier$2();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$static$1();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$new$0();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerKeep
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
- Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 7, attributes: 5
- public final java.util.function.Supplier<java.lang.Integer> mSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0011) ACC_PUBLIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public java.util.function.Supplier<java.lang.Integer> getSupplier();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- private static java.lang.Integer lambda$getSupplier_static$3();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$getSupplier$2();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$static$1();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static java.lang.Integer lambda$new$0();
- descriptor: ()Ljava/lang/Integer;
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
- x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerKeep
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
- Compiled from "TinyFrameworkMethodCallReplace.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 4
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static void startThread(java.lang.Thread);
- descriptor: (Ljava/lang/Thread;)V
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int add(int, int);
- descriptor: (II)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
-SourceFile: "TinyFrameworkMethodCallReplace.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
- Compiled from "TinyFrameworkMethodCallReplace.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 5
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception;
- descriptor: ()Z
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Exceptions:
- throws java.lang.Exception
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int staticMethodCallReplaceTester();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
- descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
- flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkMethodCallReplace.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
- Compiled from "TinyFrameworkNative.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 10, attributes: 3
- int value;
- descriptor: I
- flags: (0x0000)
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static native int nativeAddTwo(int);
- descriptor: (I)I
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddTwo_should_be_like_this(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static native long nativeLongPlus(long, long);
- descriptor: (JJ)J
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static long nativeLongPlus_should_be_like_this(long, long);
- descriptor: (JJ)J
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=4, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public void setValue(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public native int nativeNonStaticAddToValue(int);
- descriptor: (I)I
- flags: (0x0101) ACC_PUBLIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int nativeNonStaticAddToValue_should_be_like_this(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static void nativeStillNotSupported_should_be_like_this();
- descriptor: ()V
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static native byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkNative.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
- x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
- value="TinyFrameworkNative_host"
- )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 1, attributes: 4
- public int value;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 1, attributes: 5
- public int value;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
- descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
- descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- MethodParameters:
- Name Flags
- <no name> final mandated
-}
-InnerClasses:
- public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 5
- public int value;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
- super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
- interfaces: 0, fields: 0, methods: 1, attributes: 4
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
- Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 4, attributes: 5
- public final java.util.function.Supplier<java.lang.Integer> mSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0011) ACC_PUBLIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
- descriptor: Ljava/util/function/Supplier;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.util.function.Supplier<java.lang.Integer> getSupplier();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
- descriptor: ()Ljava/util/function/Supplier;
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
- #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
- Compiled from "TinyFrameworkPackageRedirect.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int foo(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkPackageRedirect.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
- Compiled from "TinyFrameworkRenamedClassCaller.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int foo(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkRenamedClassCaller.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
- Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.A
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
- Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
- Compiled from "C1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C1.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
- Compiled from "C2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
- super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C2.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
- Compiled from "C3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
- super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C3.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
- Compiled from "CA.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CA.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
- Compiled from "CB.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CB.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
- Compiled from "I1.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I1.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
- Compiled from "I2.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
- super_class: #x // java/lang/Object
- interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I2.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
- Compiled from "I3.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
- super_class: #x // java/lang/Object
- interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I3.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
- Compiled from "IA.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IA.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
- Compiled from "IB.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
- minor version: 0
- major version: 61
- flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
- this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IB.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/unsupported/UnsupportedClass.class
- Compiled from "UnsupportedClass.java"
-public class com.unsupported.UnsupportedClass
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/unsupported/UnsupportedClass
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.unsupported.UnsupportedClass(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int getValue();
- descriptor: ()I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "UnsupportedClass.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
- Compiled from "TinyFrameworkToBeRenamed.java"
-public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 3
- private final int mValue;
- descriptor: I
- flags: (0x0012) ACC_PRIVATE, ACC_FINAL
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
- descriptor: (I)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=2, args_size=2
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int getValue();
- descriptor: ()I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkToBeRenamed.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
similarity index 76%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
rename to tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index d12a23d..41f459a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -22,12 +22,12 @@
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "HostSideTestClassLoadHook.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
@@ -58,7 +58,7 @@
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
@@ -67,13 +67,44 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
- Compiled from "HostSideTestNativeSubstitutionClass.java"
-public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+## Class: android/hosttest/annotation/HostSideTestRedirect.class
+ Compiled from "HostSideTestRedirect.java"
+public interface android.hosttest.annotation.HostSideTestRedirect extends java.lang.annotation.Annotation
minor version: 0
major version: 61
flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ this_class: #x // android/hosttest/annotation/HostSideTestRedirect
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestRedirect
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestRedirect.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestRedirectionClass.class
+ Compiled from "HostSideTestRedirectionClass.java"
+public interface android.hosttest.annotation.HostSideTestRedirectionClass extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestRedirectionClass
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 2, attributes: 2
private static {};
@@ -81,7 +112,7 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ x: ldc #x // class android/hosttest/annotation/HostSideTestRedirectionClass
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -91,12 +122,12 @@
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
-SourceFile: "HostSideTestNativeSubstitutionClass.java"
+SourceFile: "HostSideTestRedirectionClass.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
@@ -127,7 +158,7 @@
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
@@ -136,13 +167,13 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestStub.class
- Compiled from "HostSideTestStub.java"
-public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
+## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class
+ Compiled from "HostSideTestStaticInitializerKeep.java"
+public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation
minor version: 0
major version: 61
flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestStub
+ this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 1, attributes: 2
private static {};
@@ -150,15 +181,15 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class android/hosttest/annotation/HostSideTestStub
+ x: ldc #x // class android/hosttest/annotation/HostSideTestStaticInitializerKeep
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
-SourceFile: "HostSideTestStub.java"
+SourceFile: "HostSideTestStaticInitializerKeep.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
@@ -191,12 +222,12 @@
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "HostSideTestSubstitute.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD]
@@ -227,7 +258,7 @@
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
@@ -258,38 +289,7 @@
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- x: #x(#x=[e#x.#x])
- java.lang.annotation.Target(
- value=[Ljava/lang/annotation/ElementType;.TYPE]
- )
- x: #x(#x=e#x.#x)
- java.lang.annotation.Retention(
- value=Ljava/lang/annotation/RetentionPolicy;.CLASS
- )
-## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
- Compiled from "HostSideTestWholeClassStub.java"
-public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
- minor version: 0
- major version: 61
- flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub
- super_class: #x // java/lang/Object
- interfaces: 1, fields: 0, methods: 1, attributes: 2
- private static {};
- descriptor: ()V
- flags: (0x000a) ACC_PRIVATE, ACC_STATIC
- Code:
- stack=2, locals=0, args_size=0
- x: ldc #x // class android/hosttest/annotation/HostSideTestWholeClassStub
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- x: return
-}
-SourceFile: "HostSideTestWholeClassStub.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
@@ -313,7 +313,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -336,9 +336,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int addTwo(int);
descriptor: (I)I
@@ -360,9 +358,7 @@
11 4 0 a I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
@@ -370,9 +366,7 @@
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
Compiled from "IPretendingAidl.java"
@@ -389,7 +383,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -412,9 +406,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int addOne(int);
descriptor: (I)I
@@ -436,9 +428,7 @@
11 4 0 a I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
@@ -446,9 +436,7 @@
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
Compiled from "IPretendingAidl.java"
@@ -465,7 +453,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
@@ -475,9 +463,7 @@
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
@@ -495,9 +481,7 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.android.hoststubgen.test.tinyframework.R$Nested();
descriptor: ()V
@@ -518,9 +502,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -546,18 +528,14 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
SourceFile: "R.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/R
## Class: com/android/hoststubgen/test/tinyframework/R.class
Compiled from "R.java"
@@ -574,7 +552,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/R
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -597,239 +575,31 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/R;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
SourceFile: "R.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/R$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
- Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
- minor version: 0
- major version: 61
- flags: (0x0020) ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 4
- private static {};
- descriptor: ()V
- flags: (0x000a) ACC_PRIVATE, ACC_STATIC
- Code:
- stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- x: return
-
- private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
- descriptor: ()V
- flags: (0x0002) ACC_PRIVATE
- Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- x: ldc #x // String <init>
- x: ldc #x // String ()V
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOneKeep();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- x: ldc #x // String getOneKeep
- x: ldc #x // String ()I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- x: ldc #x // String getOneKeep
- x: ldc #x // String ()I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iconst_1
- x: ireturn
- LineNumberTable:
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestKeep
-
- public static int getOneStub();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- x: ldc #x // String getOneStub
- x: ldc #x // String ()I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: iconst_1
- x: ireturn
- LineNumberTable:
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-}
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
- Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
+ Compiled from "TinyFrameworkAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 5
- private static {};
- descriptor: ()V
- flags: (0x000a) ACC_PRIVATE, ACC_STATIC
- Code:
- stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- x: return
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
- x: ldc #x // String <init>
- x: ldc #x // String ()V
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOne_withCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
- x: ldc #x // String getOne_withCheck
- x: ldc #x // String ()I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- x: ireturn
- LineNumberTable:
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int getOne_noCheck();
- descriptor: ()I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
- x: ldc #x // String getOne_noCheck
- x: ldc #x // String ()I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- x: ireturn
- LineNumberTable:
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
- private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
- com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
- Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 8, attributes: 3
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
+ interfaces: 0, fields: 1, methods: 6, attributes: 3
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestKeep
@@ -839,20 +609,20 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
x: ldc #x // String <init>
x: ldc #x // String ()V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -861,68 +631,29 @@
x: invokespecial #x // Method java/lang/Object."<init>":()V
x: aload_0
x: iconst_1
- x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
x: putfield #x // Field keep:I
x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
x: ldc #x // String addOne
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
- 11 6 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- x: ldc #x // String addOneInner
- x: ldc #x // String (I)I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- x: ldc #x // String addOneInner
- x: ldc #x // String (I)I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: iload_1
x: iconst_1
x: iadd
@@ -930,11 +661,11 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
- 26 4 1 value I
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+ 11 4 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestKeep
@@ -944,7 +675,7 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
x: ldc #x // String addTwo
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -956,22 +687,20 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
11 4 1 value I
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
x: ldc #x // String nativeAddThree
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -988,26 +717,18 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.String unsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
x: ldc #x // String unsupportedMethod
x: ldc #x // String ()Ljava/lang/String;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- x: ldc #x // String unsupportedMethod
- x: ldc #x // String ()Ljava/lang/String;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
x: new #x // class java/lang/RuntimeException
x: dup
@@ -1018,307 +739,22 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestThrow
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- x: ldc #x // String visibleButUsesUnsupportedMethod
- x: ldc #x // String ()Ljava/lang/String;
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
- RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestStub
}
-SourceFile: "TinyFrameworkClassAnnotations.java"
+SourceFile: "TinyFrameworkAnnotations.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
- Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
- minor version: 0
- major version: 61
- flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- super_class: #x // java/lang/Object
- interfaces: 0, fields: 3, methods: 9, attributes: 3
- public int stub;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int keep;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int remove;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- private static {};
- descriptor: ()V
- flags: (0x000a) ACC_PRIVATE, ACC_STATIC
- Code:
- stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- x: return
-
- public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String <init>
- x: ldc #x // String ()V
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: invokespecial #x // Method java/lang/Object."<init>":()V
- x: aload_0
- x: iconst_1
- x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
- x: putfield #x // Field keep:I
- x: return
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOne(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String addOne
- x: ldc #x // String (I)I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 11 6 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String addOneInner
- x: ldc #x // String (I)I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: iload_1
- x: iconst_1
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 11 4 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public void toBeRemoved(java.lang.String);
- descriptor: (Ljava/lang/String;)V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String toBeRemoved
- x: ldc #x // String (Ljava/lang/String;)V
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- x: athrow
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 11 8 1 foo Ljava/lang/String;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addTwo(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String addTwo
- x: ldc #x // String (I)I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: iload_1
- x: iconst_2
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- 11 4 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static int nativeAddThree(int);
- descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String nativeAddThree
- x: ldc #x // String (I)I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: iload_0
- x: iconst_3
- x: iadd
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 4 0 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String unsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String unsupportedMethod
- x: ldc #x // String ()Ljava/lang/String;
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String This value shouldn\'t be seen on the host side.
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
- x: ldc #x // String visibleButUsesUnsupportedMethod
- x: ldc #x // String ()Ljava/lang/String;
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
@@ -1334,9 +770,7 @@
Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
descriptor: ()V
@@ -1357,9 +791,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static void onClassLoaded(java.lang.Class<?>);
descriptor: (Ljava/lang/Class;)V
@@ -1386,9 +818,7 @@
Signature: #x // (Ljava/lang/Class<*>;)V
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -1411,19 +841,145 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 5, attributes: 3
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+ x: ldc #x // String addOne
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ 11 4 1 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+ x: ldc #x // String addTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+ 11 4 1 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestThrow
+}
+SourceFile: "TinyFrameworkClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
Compiled from "TinyFrameworkClassWithInitializerDefault.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
@@ -1438,35 +994,29 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.lang.Object sObject;
descriptor: Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
}
SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
Compiled from "TinyFrameworkClassWithInitializerStub.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
@@ -1481,24 +1031,20 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.lang.Object sObject;
descriptor: Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
static {};
descriptor: ()V
@@ -1526,21 +1072,19 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkClassWithInitializerStub.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
@@ -1557,43 +1101,37 @@
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private final java.lang.String mLongName;
descriptor: Ljava/lang/String;
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestKeep
@@ -1603,7 +1141,7 @@
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestKeep
@@ -1613,9 +1151,7 @@
flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1634,9 +1170,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1659,9 +1193,7 @@
11 10 0 name Ljava/lang/String;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> mandated
@@ -1696,12 +1228,10 @@
Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
MethodParameters:
Name Flags
<no name> synthetic
@@ -1728,12 +1258,10 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.lang.String getShortName();
descriptor: ()Ljava/lang/String;
@@ -1754,12 +1282,10 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1789,9 +1315,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -1826,7 +1350,7 @@
x: dup
x: ldc #x // String BLUE
x: iconst_2
- x: ldc #x // String Blue
+ x: ldc #x // String Blue
x: ldc #x // String B
x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
x: putstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1836,20 +1360,16 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
SourceFile: "TinyFrameworkEnumComplex.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
Compiled from "TinyFrameworkEnumSimple.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
@@ -1864,33 +1384,27 @@
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
@@ -1909,9 +1423,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
@@ -1934,9 +1446,7 @@
11 10 0 name Ljava/lang/String;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> mandated
@@ -1963,9 +1473,7 @@
Signature: #x // ()V
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> synthetic
@@ -1995,9 +1503,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -2030,20 +1536,16 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
SourceFile: "TinyFrameworkEnumSimple.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
@@ -2059,7 +1561,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -2082,9 +1584,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int testException();
descriptor: ()I
@@ -2120,19 +1620,15 @@
22 11 0 e Ljava/lang/Exception;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
@@ -2141,22 +1637,13 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 17, attributes: 2
+ interfaces: 0, fields: 1, methods: 15, attributes: 2
public int stub;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int keep;
- descriptor: I
- flags: (0x0001) ACC_PUBLIC
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -2164,7 +1651,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
@@ -2186,19 +1673,14 @@
x: aload_0
x: iconst_1
x: putfield #x // Field stub:I
- x: aload_0
- x: iconst_2
- x: putfield #x // Field keep:I
x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int addOne(int);
descriptor: (I)I
@@ -2210,37 +1692,6 @@
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: iload_1
- x: invokevirtual #x // Method addOneInner:(I)I
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
- 11 6 1 value I
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public int addOneInner(int);
- descriptor: (I)I
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String addOneInner
- x: ldc #x // String (I)I
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String addOneInner
- x: ldc #x // String (I)I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: iload_1
x: iconst_1
x: iadd
@@ -2248,11 +1699,11 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
- 26 4 1 value I
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 11 4 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.String toBeIgnoredObj();
descriptor: ()Ljava/lang/String;
@@ -2270,9 +1721,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public void toBeIgnoredV();
descriptor: ()V
@@ -2289,9 +1738,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public boolean toBeIgnoredZ();
descriptor: ()Z
@@ -2309,9 +1756,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public byte toBeIgnoredB();
descriptor: ()B
@@ -2329,9 +1774,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public char toBeIgnoredC();
descriptor: ()C
@@ -2349,9 +1792,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public short toBeIgnoredS();
descriptor: ()S
@@ -2369,9 +1810,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int toBeIgnoredI();
descriptor: ()I
@@ -2389,9 +1828,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public float toBeIgnoredF();
descriptor: ()F
@@ -2409,9 +1846,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public double toBeIgnoredD();
descriptor: ()D
@@ -2429,9 +1864,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int addTwo(int);
descriptor: (I)I
@@ -2439,7 +1872,7 @@
Code:
stack=4, locals=2, args_size=2
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String addTwo
+ x: ldc #x // String addTwo
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
@@ -2456,9 +1889,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeAddThree(int);
descriptor: (I)I
@@ -2466,7 +1897,7 @@
Code:
stack=4, locals=1, args_size=1
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String nativeAddThree
+ x: ldc #x // String nativeAddThree
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
@@ -2482,9 +1913,7 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.String unsupportedMethod();
descriptor: ()Ljava/lang/String;
@@ -2492,57 +1921,26 @@
Code:
stack=4, locals=1, args_size=1
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String unsupportedMethod
+ x: ldc #x // String unsupportedMethod
x: ldc #x // String ()Ljava/lang/String;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String unsupportedMethod
- x: ldc #x // String ()Ljava/lang/String;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- x: new #x // class java/lang/RuntimeException
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
x: dup
- x: ldc #x // String Unreachable
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public java.lang.String visibleButUsesUnsupportedMethod();
- descriptor: ()Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- x: ldc #x // String visibleButUsesUnsupportedMethod
- x: ldc #x // String ()Ljava/lang/String;
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: aload_0
- x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- x: areturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkForTextPolicy.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
Compiled from "TinyFrameworkLambdas.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
@@ -2558,12 +1956,10 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
descriptor: Ljava/util/function/Supplier;
@@ -2571,12 +1967,10 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
descriptor: ()V
@@ -2600,12 +1994,10 @@
11 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
@@ -2626,12 +2018,10 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -2649,12 +2039,10 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static java.lang.Integer lambda$getSupplier_static$3();
descriptor: ()Ljava/lang/Integer;
@@ -2672,9 +2060,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$getSupplier$2();
descriptor: ()Ljava/lang/Integer;
@@ -2692,9 +2078,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$static$1();
descriptor: ()Ljava/lang/Integer;
@@ -2712,9 +2096,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$new$0();
descriptor: ()Ljava/lang/Integer;
@@ -2732,9 +2114,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -2750,12 +2130,12 @@
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
- x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
x: return
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
@@ -2763,12 +2143,10 @@
SourceFile: "TinyFrameworkLambdas.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
BootstrapMethods:
@@ -2808,12 +2186,10 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
descriptor: Ljava/util/function/Supplier;
@@ -2821,12 +2197,10 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
descriptor: ()V
@@ -2850,12 +2224,10 @@
11 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
@@ -2876,12 +2248,10 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -2899,12 +2269,10 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
private static java.lang.Integer lambda$getSupplier_static$3();
descriptor: ()Ljava/lang/Integer;
@@ -2922,9 +2290,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$getSupplier$2();
descriptor: ()Ljava/lang/Integer;
@@ -2942,9 +2308,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$static$1();
descriptor: ()Ljava/lang/Integer;
@@ -2962,9 +2326,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static java.lang.Integer lambda$new$0();
descriptor: ()Ljava/lang/Integer;
@@ -2982,9 +2344,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -3000,12 +2360,12 @@
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
- x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
x: return
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
@@ -3013,12 +2373,10 @@
SourceFile: "TinyFrameworkLambdas.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestStub
+ android.hosttest.annotation.HostSideTestKeep
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerKeep
BootstrapMethods:
@@ -3059,7 +2417,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -3082,9 +2440,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static void startThread(java.lang.Thread);
descriptor: (Ljava/lang/Thread;)V
@@ -3108,9 +2464,7 @@
11 10 0 thread Ljava/lang/Thread;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int add(int, int);
descriptor: (II)I
@@ -3133,18 +2487,14 @@
11 4 1 b I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
Compiled from "TinyFrameworkMethodCallReplace.java"
@@ -3161,7 +2511,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -3184,9 +2534,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception;
descriptor: ()Z
@@ -3225,9 +2573,7 @@
throws java.lang.Exception
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int staticMethodCallReplaceTester();
descriptor: ()I
@@ -3246,9 +2592,7 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
@@ -3261,7 +2605,7 @@
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
x: aload_0
- x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+ x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
x: invokevirtual #x // Method java/lang/Thread.isDaemon:()Z
x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
x: return
@@ -3271,9 +2615,7 @@
11 11 0 ab Ljava/util/concurrent/atomic/AtomicBoolean;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
@@ -3281,12 +2623,10 @@
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
BootstrapMethods:
x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
@@ -3303,15 +2643,13 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 12, attributes: 3
+ interfaces: 0, fields: 1, methods: 15, attributes: 3
int value;
descriptor: I
flags: (0x0000)
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -3319,7 +2657,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -3342,9 +2680,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeAddTwo(int);
descriptor: (I)I
@@ -3363,9 +2699,10 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ android.hosttest.annotation.HostSideTestRedirect
public static int nativeAddTwo_should_be_like_this(int);
descriptor: (I)I
@@ -3386,9 +2723,7 @@
11 5 0 arg I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static long nativeLongPlus(long, long);
descriptor: (JJ)J
@@ -3408,9 +2743,10 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ android.hosttest.annotation.HostSideTestRedirect
public static long nativeLongPlus_should_be_like_this(long, long);
descriptor: (JJ)J
@@ -3433,9 +2769,7 @@
11 6 2 arg2 J
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public void setValue(int);
descriptor: (I)V
@@ -3458,9 +2792,7 @@
11 6 1 v I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int nativeNonStaticAddToValue(int);
descriptor: (I)I
@@ -3480,9 +2812,10 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ android.hosttest.annotation.HostSideTestRedirect
public int nativeNonStaticAddToValue_should_be_like_this(int);
descriptor: (I)I
@@ -3505,9 +2838,7 @@
11 6 1 arg I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static void nativeStillNotSupported();
descriptor: ()V
@@ -3519,49 +2850,46 @@
x: ldc #x // String ()V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
- x: ldc #x // String nativeStillNotSupported
- x: ldc #x // String ()V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
x: new #x // class java/lang/RuntimeException
x: dup
x: ldc #x // String Unreachable
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestThrow
+ public static native void nativeStillKeep();
+ descriptor: ()V
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
public static void nativeStillNotSupported_should_be_like_this();
descriptor: ()V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
- x: ldc #x // String nativeStillNotSupported_should_be_like_this
+ x: ldc #x // String nativeStillNotSupported_should_be_like_this
x: ldc #x // String ()V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
x: new #x // class java/lang/RuntimeException
x: dup
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
x: athrow
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static byte nativeBytePlus(byte, byte);
descriptor: (BB)B
@@ -3569,33 +2897,75 @@
Code:
stack=4, locals=2, args_size=2
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
- x: ldc #x // String nativeBytePlus
- x: ldc #x // String (BB)B
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
x: iload_0
x: iload_1
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
x: ireturn
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ android.hosttest.annotation.HostSideTestRedirect
+
+ public void notNativeRedirected();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String notNativeRedirected
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.notNativeRedirected:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V
+ x: return
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
+
+ public static void notNativeStaticRedirected();
+ descriptor: ()V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String notNativeStaticRedirected
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.notNativeStaticRedirected:()V
+ x: return
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestRedirect
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
x: #x(#x=s#x)
- android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ android.hosttest.annotation.HostSideTestRedirectionClass(
value="TinyFrameworkNative_host"
)
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
@@ -3606,7 +2976,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 6, attributes: 3
+ interfaces: 0, fields: 0, methods: 8, attributes: 3
private static {};
descriptor: ()V
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -3627,22 +2997,16 @@
x: ldc #x // String ()V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String <init>
- x: ldc #x // String ()V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: invokespecial #x // Method java/lang/Object."<init>":()V
x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeAddTwo(int);
descriptor: (I)I
@@ -3654,12 +3018,6 @@
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeAddTwo
- x: ldc #x // String (I)I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: iload_0
x: iconst_2
x: iadd
@@ -3667,10 +3025,10 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 4 0 arg I
+ 11 4 0 arg I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static long nativeLongPlus(long, long);
descriptor: (JJ)J
@@ -3682,12 +3040,6 @@
x: ldc #x // String (JJ)J
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeLongPlus
- x: ldc #x // String (JJ)J
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: lload_0
x: lload_2
x: ladd
@@ -3695,11 +3047,11 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 4 0 arg1 J
- 26 4 2 arg2 J
+ 11 4 0 arg1 J
+ 11 4 2 arg2 J
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
@@ -3711,12 +3063,6 @@
x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeNonStaticAddToValue
- x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
x: iload_1
@@ -3725,11 +3071,11 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
- 26 7 1 arg I
+ 11 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 11 7 1 arg I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static byte nativeBytePlus(byte, byte);
descriptor: (BB)B
@@ -3741,12 +3087,6 @@
x: ldc #x // String (BB)B
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeBytePlus
- x: ldc #x // String (BB)B
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: iload_0
x: iload_1
x: iadd
@@ -3755,16 +3095,51 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 arg1 B
- 26 5 1 arg2 B
+ 11 5 0 arg1 B
+ 11 5 1 arg2 B
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static void notNativeRedirected(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String notNativeRedirected
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 1 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static void notNativeStaticRedirected();
+ descriptor: ()V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String notNativeStaticRedirected
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: return
+ LineNumberTable:
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
@@ -3782,7 +3157,7 @@
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -3817,7 +3192,7 @@
11 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> final mandated
@@ -3832,22 +3207,16 @@
x: ldc #x // String ()Ljava/lang/Integer;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: iconst_1
x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
@@ -3859,22 +3228,16 @@
x: ldc #x // String ()Ljava/lang/Object;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: invokevirtual #x // Method get:()Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
@@ -3883,7 +3246,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -3923,7 +3286,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Integer get();
descriptor: ()Ljava/lang/Integer;
@@ -3935,22 +3298,16 @@
x: ldc #x // String ()Ljava/lang/Integer;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: iconst_2
x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
@@ -3962,22 +3319,16 @@
x: ldc #x // String ()Ljava/lang/Object;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: invokevirtual #x // Method get:()Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
@@ -3986,7 +3337,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -4002,7 +3353,7 @@
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -4037,7 +3388,7 @@
11 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> final mandated
@@ -4052,22 +3403,16 @@
x: ldc #x // String ()Ljava/lang/Integer;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: iconst_3
x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
@@ -4079,22 +3424,16 @@
x: ldc #x // String ()Ljava/lang/Object;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: invokevirtual #x // Method get:()Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
@@ -4103,7 +3442,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -4143,7 +3482,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Integer get();
descriptor: ()Ljava/lang/Integer;
@@ -4155,22 +3494,16 @@
x: ldc #x // String ()Ljava/lang/Integer;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: iconst_4
x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
@@ -4182,22 +3515,16 @@
x: ldc #x // String ()Ljava/lang/Object;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: invokevirtual #x // Method get:()Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
@@ -4206,7 +3533,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -4222,9 +3549,7 @@
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -4232,7 +3557,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -4259,18 +3584,14 @@
11 10 1 x I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -4280,24 +3601,20 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 2, attributes: 5
+ interfaces: 0, fields: 2, methods: 2, attributes: 4
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -4305,7 +3622,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -4335,9 +3652,7 @@
11 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
MethodParameters:
Name Flags
<no name> final mandated
@@ -4347,12 +3662,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -4392,7 +3702,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Integer get();
descriptor: ()Ljava/lang/Integer;
@@ -4404,22 +3714,16 @@
x: ldc #x // String ()Ljava/lang/Integer;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Integer;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: bipush 7
x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.lang.Object get();
descriptor: ()Ljava/lang/Object;
@@ -4431,22 +3735,16 @@
x: ldc #x // String ()Ljava/lang/Object;
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- x: ldc #x // String get
- x: ldc #x // String ()Ljava/lang/Object;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: invokevirtual #x // Method get:()Ljava/lang/Integer;
x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
@@ -4456,7 +3754,65 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 8
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -4466,15 +3822,13 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 3, attributes: 5
+ interfaces: 0, fields: 1, methods: 3, attributes: 4
public int value;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -4482,7 +3836,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -4508,9 +3862,7 @@
11 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -4530,22 +3882,16 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
- x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -4562,7 +3908,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -4587,9 +3933,7 @@
11 6 1 x I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
@@ -4597,9 +3941,7 @@
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -4616,9 +3958,7 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
descriptor: Ljava/util/function/Supplier;
@@ -4626,9 +3966,7 @@
Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
descriptor: ()V
@@ -4655,9 +3993,7 @@
11 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
@@ -4681,9 +4017,7 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
@@ -4703,9 +4037,7 @@
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
static {};
descriptor: ()V
@@ -4728,33 +4060,31 @@
LineNumberTable:
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
@@ -4776,7 +4106,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -4799,9 +4129,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int foo(int);
descriptor: (I)I
@@ -4825,19 +4153,15 @@
11 12 0 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkPackageRedirect.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
Compiled from "TinyFrameworkRenamedClassCaller.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
@@ -4853,7 +4177,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -4876,9 +4200,7 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public static int foo(int);
descriptor: (I)I
@@ -4902,19 +4224,15 @@
11 12 0 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
Compiled from "A.java"
public class com.android.hoststubgen.test.tinyframework.packagetest.A
@@ -4930,16 +4248,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/packagetest/A
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "A.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
Compiled from "A.java"
public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
@@ -4955,16 +4271,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/packagetest/sub/A
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "A.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
Compiled from "C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -4980,16 +4294,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C1
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "C1.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
Compiled from "C2.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -5005,16 +4317,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C2
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "C2.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
Compiled from "C3.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
@@ -5030,16 +4340,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C3
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "C3.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
Compiled from "CA.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
@@ -5055,16 +4363,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/CA
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "CA.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
Compiled from "CB.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
@@ -5080,16 +4386,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/CB
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "CB.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
Compiled from "Class_C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -5112,7 +4416,7 @@
SourceFile: "Class_C1.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
Compiled from "Class_C2.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
@@ -5135,7 +4439,7 @@
SourceFile: "Class_C2.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
Compiled from "Class_C3.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
@@ -5158,7 +4462,7 @@
SourceFile: "Class_C3.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
Compiled from "Class_I1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
@@ -5181,7 +4485,7 @@
SourceFile: "Class_I1.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
Compiled from "Class_I1_IA.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
@@ -5204,7 +4508,7 @@
SourceFile: "Class_I1_IA.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
Compiled from "Class_I2.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
@@ -5227,7 +4531,7 @@
SourceFile: "Class_I2.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
Compiled from "Class_I3.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
@@ -5250,7 +4554,7 @@
SourceFile: "Class_I3.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
Compiled from "I1.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
@@ -5266,16 +4570,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/I1
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "I1.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
Compiled from "I2.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
@@ -5298,9 +4600,7 @@
SourceFile: "I2.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
Compiled from "I3.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
@@ -5323,9 +4623,7 @@
SourceFile: "I3.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
Compiled from "IA.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
@@ -5341,16 +4639,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/IA
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "IA.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
Compiled from "IB.java"
public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
@@ -5366,16 +4662,14 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/IB
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
}
SourceFile: "IB.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
## Class: com/supported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.supported.UnsupportedClass
@@ -5390,7 +4684,7 @@
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
@@ -5412,12 +4706,6 @@
x: ldc #x // String (I)V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/supported/UnsupportedClass
- x: ldc #x // String <init>
- x: ldc #x // String (I)V
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: invokespecial #x // Method java/lang/Object."<init>":()V
x: aload_0
@@ -5427,11 +4715,11 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 10 0 this Lcom/supported/UnsupportedClass;
- 26 10 1 value I
+ 11 10 0 this Lcom/supported/UnsupportedClass;
+ 11 10 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int getValue();
descriptor: ()I
@@ -5443,27 +4731,21 @@
x: ldc #x // String ()I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/supported/UnsupportedClass
- x: ldc #x // String getValue
- x: ldc #x // String ()I
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
x: aload_0
x: getfield #x // Field mValue:I
x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 26 5 0 this Lcom/supported/UnsupportedClass;
+ 11 5 0 this Lcom/supported/UnsupportedClass;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "UnsupportedClass.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
@@ -5482,7 +4764,7 @@
Code:
stack=2, locals=0, args_size=0
x: ldc #x // class com/unsupported/UnsupportedClass
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -5510,9 +4792,7 @@
11 14 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int getValue();
descriptor: ()I
@@ -5535,19 +4815,15 @@
11 10 0 this Lcom/unsupported/UnsupportedClass;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "UnsupportedClass.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
Compiled from "TinyFrameworkToBeRenamed.java"
public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
@@ -5562,16 +4838,14 @@
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
private static {};
descriptor: ()V
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -5581,7 +4855,7 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
x: ldc #x // String <init>
x: ldc #x // String (I)V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -5599,16 +4873,14 @@
11 10 1 value I
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
public int getValue();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
x: ldc #x // String getValue
x: ldc #x // String ()I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -5622,16 +4894,12 @@
11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkToBeRenamed.java"
RuntimeVisibleAnnotations:
x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
RuntimeInvisibleAnnotations:
x: #x()
- android.hosttest.annotation.HostSideTestWholeClassStub
+ android.hosttest.annotation.HostSideTestWholeClassKeep
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index f064433..3c138d2 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -1,9 +1,8 @@
-class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy stub
- field stub stub
- field keep keep
+class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy keep
+ field stub keep
# field remove remove # Implicitly remove
- method <init> ()V stub
- method addOne (I)I stub
+ method <init> ()V keep
+ method addOne (I)I keep
method addOneInner (I)I keep
method toBeRemoved (Ljava/lang/String;)V remove
method addTwo (I)I @addTwo_host
@@ -11,7 +10,7 @@
method nativeAddThree (I)I @addThree_host
# method addThree_host (I)I # used as a substitute
method unsupportedMethod ()Ljava/lang/String; throw
- method visibleButUsesUnsupportedMethod ()Ljava/lang/String; stub
+ method visibleButUsesUnsupportedMethod ()Ljava/lang/String; keep
method toBeIgnoredObj ()Ljava/lang/String; ignore
method toBeIgnoredV ()V ignore
method toBeIgnoredZ ()Z ignore
@@ -27,22 +26,22 @@
class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
# Heuristics rule: Stub all the AIDL classes.
-class :aidl stubclass
+class :aidl keepclass
# Heuristics rule: Stub all the R classes.
-class :r stubclass
+class :r keepclass
# Default is "remove", so let's put all the base classes / interfaces in the stub first.
-class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.C3 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.CA stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.CB stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.I1 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.I2 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.I3 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.IA stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.IB stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.C1 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.C2 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.C3 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.CA keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.CB keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.I1 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.I2 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.I3 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.IA keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.IB keep
# Then define inheritance based policies.
class *com.android.hoststubgen.test.tinyframework.subclasstest.C1 keep
@@ -52,15 +51,15 @@
class *com.android.hoststubgen.test.tinyframework.subclasstest.IA remove
# Test package directive
-package com.android.hoststubgen.test.tinyframework.packagetest stub
+package com.android.hoststubgen.test.tinyframework.packagetest keep
class com.android.hoststubgen.test.tinyframework.packagetest.B remove
class com.android.hoststubgen.test.tinyframework.packagetest.sub.B remove
# The following rules are the same as above
-# class com.android.hoststubgen.test.tinyframework.packagetest.A stub
-# class com.android.hoststubgen.test.tinyframework.packagetest.sub.A stub
+# class com.android.hoststubgen.test.tinyframework.packagetest.A keep
+# class com.android.hoststubgen.test.tinyframework.packagetest.sub.A keep
# Used to test method call replacement.
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace stubclass
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace keepclass
method originalAdd (II)I @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.add
# Used to test method call replacement.
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
index 872bbf8..80ebf3a 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
@@ -43,8 +43,7 @@
tiny_framework_classes=$out/tiny-framework/classes/
tiny_framework_jar=$out/tiny-framework.jar
-tiny_framework_host_stub_jar=$out/tiny-framework_host_stub.jar
-tiny_framework_host_impl_jar=$out/tiny-framework_host_impl.jar
+tiny_framework_host_jar=$out/tiny-framework_host.jar
tiny_test_classes=$out/tiny-test/classes/
tiny_test_jar=$out/tiny-test.jar
@@ -87,8 +86,7 @@
run $HOSTSTUBGEN \
@../hoststubgen-standard-options.txt \
--in-jar $tiny_framework_jar \
- --out-stub-jar $tiny_framework_host_stub_jar \
- --out-impl-jar $tiny_framework_host_impl_jar \
+ --out-jar $tiny_framework_host_jar \
--policy-override-file policy-override-tiny-framework.txt \
--gen-keep-all-file out/tiny-framework_keep_all.txt \
--gen-input-dump-file out/tiny-framework_dump.txt \
@@ -97,14 +95,14 @@
$HOSTSTUBGEN_OPTS
# Extract the jar files, so we can look into them.
-extract $tiny_framework_host_stub_jar $tiny_framework_host_impl_jar
+extract $tiny_framework_host_jar
# Build the test
echo "# Building tiny-test..."
run $JAVAC \
-cp $( \
join : \
- $tiny_framework_host_stub_jar \
+ $tiny_framework_jar \
"${test_compile_classpaths[@]}" \
) \
-d $tiny_test_classes \
@@ -124,7 +122,7 @@
-cp $( \
join : \
$tiny_test_jar \
- $tiny_framework_host_impl_jar \
+ $tiny_framework_host_jar \
"${test_compile_classpaths[@]}" \
"${test_runtime_classpaths[@]}" \
) \
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
similarity index 80%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
rename to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
index 6d8a48a..ed0fa26 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
@@ -18,39 +18,30 @@
import android.hosttest.annotation.HostSideTestClassLoadHook;
import android.hosttest.annotation.HostSideTestKeep;
import android.hosttest.annotation.HostSideTestRemove;
-import android.hosttest.annotation.HostSideTestStub;
import android.hosttest.annotation.HostSideTestSubstitute;
import android.hosttest.annotation.HostSideTestThrow;
/**
* Test without class-wide annotations.
*/
-@HostSideTestStub
+@HostSideTestKeep
@HostSideTestClassLoadHook(
"com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
-public class TinyFrameworkClassAnnotations {
- @HostSideTestStub
- public TinyFrameworkClassAnnotations() {
+public class TinyFrameworkAnnotations {
+ @HostSideTestKeep
+ public TinyFrameworkAnnotations() {
}
- @HostSideTestStub
- public int stub = 1;
-
@HostSideTestKeep
- public int keep = 2;
+ public int keep = 1;
// Members will be deleted by default.
// Deleted fields cannot have an initial value, because otherwise .ctor will fail to set it at
// runtime.
public int remove;
- @HostSideTestStub
- public int addOne(int value) {
- return addOneInner(value);
- }
-
@HostSideTestKeep
- public int addOneInner(int value) {
+ public int addOne(int value) {
return value + 1;
}
@@ -59,7 +50,6 @@
throw new RuntimeException();
}
- @HostSideTestStub
@HostSideTestSubstitute(suffix = "_host")
public int addTwo(int value) {
throw new RuntimeException("not supported on host side");
@@ -69,7 +59,6 @@
return value + 2;
}
- @HostSideTestStub
@HostSideTestSubstitute(suffix = "_host")
public static native int nativeAddThree(int value);
@@ -82,9 +71,4 @@
public String unsupportedMethod() {
return "This value shouldn't be seen on the host side.";
}
-
- @HostSideTestStub
- public String visibleButUsesUnsupportedMethod() {
- return unsupportedMethod();
- }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java
deleted file mode 100644
index f530207..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2023 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.hoststubgen.test.tinyframework;
-
-import android.hosttest.annotation.HostSideTestKeep;
-import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
-
-/**
- * Used by the benchmark.
- */
-@HostSideTestWholeClassStub
-public class TinyFrameworkCallerCheck {
-
- /**
- * This method uses an inner method (which has the caller check).
- *
- * Benchmark result: 768ns
- */
- public static int getOne_withCheck() {
- return Impl.getOneKeep();
- }
-
- /**
- * This method doesn't have any caller check.
- *
- * Benchmark result: 2ns
- */
- public static int getOne_noCheck() {
- return Impl.getOneStub();
- }
-
- private static class Impl {
- @HostSideTestKeep
- public static int getOneKeep() {
- return 1;
- }
-
- @HostSideTestStub
- public static int getOneStub() {
- return 1;
- }
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java
deleted file mode 100644
index 145b65a..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2023 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.hoststubgen.test.tinyframework;
-
-import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestSubstitute;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
-
-@HostSideTestWholeClassStub
-public class TinyFrameworkClassClassWideAnnotations {
- public TinyFrameworkClassClassWideAnnotations() {
- }
-
- public int stub = 1;
-
- public int keep = 2;
-
- // Cannot have an initial value, because otherwise .ctor will fail to set it at runtime.
- public int remove;
-
- // @Stub
- public int addOne(int value) {
- return addOneInner(value);
- }
-
- // @Keep
- public int addOneInner(int value) {
- return value + 1;
- }
-
- // @Remove
- public void toBeRemoved(String foo) {
- throw new RuntimeException();
- }
-
- @HostSideTestStub
- @HostSideTestSubstitute(suffix = "_host")
- public int addTwo(int value) {
- throw new RuntimeException("not supported on host side");
- }
-
- public int addTwo_host(int value) {
- return value + 2;
- }
-
- @HostSideTestStub
- @HostSideTestSubstitute(suffix = "_host")
- public static native int nativeAddThree(int value);
-
- public static int nativeAddThree_host(int value) {
- return value + 3;
- }
-
- public String unsupportedMethod() {
- return "This value shouldn't be seen on the host side.";
- }
-
- public String visibleButUsesUnsupportedMethod() {
- return unsupportedMethod();
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
index 98fc634..f734790 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
@@ -15,12 +15,12 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
import java.util.HashSet;
import java.util.Set;
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
public class TinyFrameworkClassLoadHook {
private TinyFrameworkClassLoadHook() {
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
new file mode 100644
index 0000000..e83163e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestRemove;
+import android.hosttest.annotation.HostSideTestSubstitute;
+import android.hosttest.annotation.HostSideTestThrow;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
+
+@HostSideTestWholeClassKeep
+public class TinyFrameworkClassWideAnnotations {
+ public TinyFrameworkClassWideAnnotations() {
+ }
+
+ public int keep = 1;
+
+ @HostSideTestRemove
+ public int remove;
+
+ public int addOne(int value) {
+ return value + 1;
+ }
+
+ @HostSideTestSubstitute(suffix = "_host")
+ public int addTwo(int value) {
+ throw new RuntimeException("not supported on host side");
+ }
+
+ public int addTwo_host(int value) {
+ return value + 2;
+ }
+
+ @HostSideTestRemove
+ public void toBeRemoved(String foo) {
+ throw new RuntimeException();
+ }
+
+ @HostSideTestThrow
+ public String unsupportedMethod() {
+ return "This value shouldn't be seen on the host side.";
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
index 8324ed9..3df21d9 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
@@ -15,18 +15,16 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestClassLoadHook;
-import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestKeep;
-@HostSideTestStub
+@HostSideTestKeep
public class TinyFrameworkClassWithInitializerDefault {
static {
sInitialized = true;
}
- @HostSideTestStub
+ @HostSideTestKeep
public static boolean sInitialized;
- @HostSideTestStub
+ @HostSideTestKeep
public static Object sObject = new Object();
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
index ea1ad93..cc665de 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
@@ -16,20 +16,20 @@
package com.android.hoststubgen.test.tinyframework;
import android.hosttest.annotation.HostSideTestClassLoadHook;
+import android.hosttest.annotation.HostSideTestKeep;
import android.hosttest.annotation.HostSideTestStaticInitializerKeep;
-import android.hosttest.annotation.HostSideTestStub;
@HostSideTestClassLoadHook(
"com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
-@HostSideTestStub
+@HostSideTestKeep
@HostSideTestStaticInitializerKeep
public class TinyFrameworkClassWithInitializerStub {
static {
sInitialized = true;
}
- @HostSideTestStub
+ @HostSideTestKeep
public static boolean sInitialized;
- @HostSideTestStub
+ @HostSideTestKeep
public static Object sObject = new Object();
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
index 51f4818..f833ad8 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
@@ -16,15 +16,14 @@
package com.android.hoststubgen.test.tinyframework;
import android.hosttest.annotation.HostSideTestKeep;
-import android.hosttest.annotation.HostSideTestStub;
-@HostSideTestStub
+@HostSideTestKeep
public enum TinyFrameworkEnumComplex {
- @HostSideTestStub
+ @HostSideTestKeep
RED("Red", "R"),
- @HostSideTestStub
+ @HostSideTestKeep
GREEN("Green", "G"),
- @HostSideTestStub
+ @HostSideTestKeep
BLUE("Blue", "B");
@HostSideTestKeep
@@ -33,18 +32,18 @@
@HostSideTestKeep
private final String mShortName;
- @HostSideTestStub
+ @HostSideTestKeep
TinyFrameworkEnumComplex(String longName, String shortName) {
mLongName = longName;
mShortName = shortName;
}
- @HostSideTestStub
+ @HostSideTestKeep
public String getLongName() {
return mLongName;
}
- @HostSideTestStub
+ @HostSideTestKeep
public String getShortName() {
return mShortName;
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
index f440d86..c023169 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
@@ -15,12 +15,12 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestStub;
+import android.hosttest.annotation.HostSideTestKeep;
-@HostSideTestStub
+@HostSideTestKeep
public enum TinyFrameworkEnumSimple {
- @HostSideTestStub
+ @HostSideTestKeep
CAT,
- @HostSideTestStub
+ @HostSideTestKeep
DOG,
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
index 909d3b4..f7cae7d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
@@ -15,9 +15,9 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
public class TinyFrameworkExceptionTester {
public static int testException() {
try {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
index 1977c90..ec1efba 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
@@ -24,17 +24,11 @@
public int stub = 1;
- public int keep = 2;
-
// Removed fields cannot have an initial value, because otherwise .ctor will fail to set it at
// runtime.
public int remove;
public int addOne(int value) {
- return addOneInner(value);
- }
-
- public int addOneInner(int value) {
return value + 1;
}
@@ -95,8 +89,4 @@
public String unsupportedMethod() {
return "This value shouldn't be seen on the host side.";
}
-
- public String visibleButUsesUnsupportedMethod() {
- return unsupportedMethod();
- }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
index 0d1203b..1ca653e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
@@ -15,8 +15,8 @@
*/
package com.android.hoststubgen.test.tinyframework;
+import android.hosttest.annotation.HostSideTestKeep;
import android.hosttest.annotation.HostSideTestStaticInitializerKeep;
-import android.hosttest.annotation.HostSideTestStub;
import java.util.function.Supplier;
@@ -28,48 +28,48 @@
*
* Implicit filter should take care of them.
*/
-@HostSideTestStub
+@HostSideTestKeep
@HostSideTestStaticInitializerKeep
public class TinyFrameworkLambdas {
- @HostSideTestStub
+ @HostSideTestKeep
public TinyFrameworkLambdas() {
}
- @HostSideTestStub
+ @HostSideTestKeep
public final Supplier<Integer> mSupplier = () -> 1;
- @HostSideTestStub
+ @HostSideTestKeep
public static final Supplier<Integer> sSupplier = () -> 2;
- @HostSideTestStub
+ @HostSideTestKeep
public Supplier<Integer> getSupplier() {
return () -> 3;
}
- @HostSideTestStub
+ @HostSideTestKeep
public static Supplier<Integer> getSupplier_static() {
return () -> 4;
}
- @HostSideTestStub
+ @HostSideTestKeep
@HostSideTestStaticInitializerKeep
public static class Nested {
- @HostSideTestStub
+ @HostSideTestKeep
public Nested() {
}
- @HostSideTestStub
+ @HostSideTestKeep
public final Supplier<Integer> mSupplier = () -> 5;
- @HostSideTestStub
+ @HostSideTestKeep
public static final Supplier<Integer> sSupplier = () -> 6;
- @HostSideTestStub
+ @HostSideTestKeep
public Supplier<Integer> getSupplier() {
return () -> 7;
}
- @HostSideTestStub
+ @HostSideTestKeep
public static Supplier<Integer> getSupplier_static() {
return () -> 8;
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
index 1ff3744..57c69a3 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
@@ -15,11 +15,11 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
import java.util.concurrent.atomic.AtomicBoolean;
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
public class TinyFrameworkMethodCallReplace {
// This method should return true.
public static boolean nonStaticMethodCallReplaceTester() throws Exception {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
index 09ee183..04a551c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
@@ -15,19 +15,23 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestNativeSubstitutionClass;
+import android.hosttest.annotation.HostSideTestRedirect;
+import android.hosttest.annotation.HostSideTestRedirectionClass;
import android.hosttest.annotation.HostSideTestThrow;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
-@HostSideTestWholeClassStub
-@HostSideTestNativeSubstitutionClass("TinyFrameworkNative_host")
+@HostSideTestWholeClassKeep
+@HostSideTestRedirectionClass("TinyFrameworkNative_host")
public class TinyFrameworkNative {
+
+ @HostSideTestRedirect
public static native int nativeAddTwo(int arg);
public static int nativeAddTwo_should_be_like_this(int arg) {
return TinyFrameworkNative_host.nativeAddTwo(arg);
}
+ @HostSideTestRedirect
public static native long nativeLongPlus(long arg1, long arg2);
public static long nativeLongPlus_should_be_like_this(long arg1, long arg2) {
@@ -40,6 +44,7 @@
this.value = v;
}
+ @HostSideTestRedirect
public native int nativeNonStaticAddToValue(int arg);
public int nativeNonStaticAddToValue_should_be_like_this(int arg) {
@@ -49,9 +54,22 @@
@HostSideTestThrow
public static native void nativeStillNotSupported();
+ public static native void nativeStillKeep();
+
public static void nativeStillNotSupported_should_be_like_this() {
throw new RuntimeException();
}
+ @HostSideTestRedirect
public static native byte nativeBytePlus(byte arg1, byte arg2);
+
+ @HostSideTestRedirect
+ public void notNativeRedirected() {
+ throw new RuntimeException();
+ }
+
+ @HostSideTestRedirect
+ public static void notNativeStaticRedirected() {
+ throw new RuntimeException();
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
index b23c216..c7a29a1 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
@@ -17,8 +17,6 @@
import android.hosttest.annotation.HostSideTestWholeClassKeep;
-// TODO: This annotation shouldn't be needed.
-// We should infer it from HostSideTestNativeSubstitutionClass.
@HostSideTestWholeClassKeep
public class TinyFrameworkNative_host {
public static int nativeAddTwo(int arg) {
@@ -38,4 +36,10 @@
public static byte nativeBytePlus(byte arg1, byte arg2) {
return (byte) (arg1 + arg2);
}
+
+ public static void notNativeRedirected(TinyFrameworkNative source) {
+ }
+
+ public static void notNativeStaticRedirected() {
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
index e1c48bb..c1ea2ee 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
@@ -15,11 +15,11 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
import java.util.function.Supplier;
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
public class TinyFrameworkNestedClasses {
public final Supplier<Integer> mSupplier = new Supplier<Integer>() {
@Override
@@ -34,6 +34,7 @@
return 2;
}
};
+
public Supplier<Integer> getSupplier() {
return new Supplier<Integer>() {
@Override
@@ -52,12 +53,10 @@
};
}
- @HostSideTestWholeClassStub
public class InnerClass {
public int value = 5;
}
- @HostSideTestWholeClassStub
public static class StaticNestedClass {
public int value = 6;
@@ -70,6 +69,10 @@
}
};
}
+
+ public static class Double$NestedClass {
+ public int value = 8;
+ }
}
public static class BaseClass {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
index a82be54..941fcff 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
@@ -15,9 +15,9 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
public class TinyFrameworkPackageRedirect {
/**
* A method that uses "unsupported" class. HostStubGen will redirect them to the "supported"
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
index 31a164a..707bc0e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
@@ -15,9 +15,9 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
public class TinyFrameworkRenamedClassCaller {
/** Calls the class that'll be renamed. */
public static int foo(int value) {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
index 1430bcb..8319ced 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
@@ -15,12 +15,12 @@
*/
package com.android.hoststubgen.test.tinyframework;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
/**
* This class will be renamed by the "rename" directive in the policy file.
*/
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
public class TinyFrameworkToBeRenamed {
private final int mValue;
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
index 0409b02..92f41ac 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
@@ -15,10 +15,10 @@
*/
package com.unsupported;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
// Used for testing --package-redirect.
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
public class UnsupportedClass {
public UnsupportedClass(int value) {
throw new RuntimeException("This class is not supported");
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
new file mode 100644
index 0000000..1ae0493
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 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.hoststubgen.test.tinyframework;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class TinyFrameworkAnnotationsTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testSimple() {
+ TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+ assertThat(tfc.addOne(1)).isEqualTo(2);
+ assertThat(tfc.keep).isEqualTo(1);
+ }
+
+ @Test
+ public void testRemove() {
+ TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+ assertThrows(NoSuchMethodError.class, () -> tfc.toBeRemoved("abc"));
+ assertThrows(NoSuchFieldError.class, () -> tfc.remove = 1);
+ }
+
+ @Test
+ public void testSubstitute() {
+ TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+ assertThat(tfc.addTwo(1)).isEqualTo(3);
+ }
+
+ @Test
+ public void testSubstituteNative() {
+ TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+ assertThat(tfc.nativeAddThree(1)).isEqualTo(4);
+ }
+
+ @Test
+ public void testUnsupportedMethod() {
+ TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage("not yet supported");
+ tfc.unsupportedMethod();
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
deleted file mode 100644
index d57735b..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2023 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.hoststubgen.test.tinyframework;
-
-import org.junit.Test;
-
-import java.text.DecimalFormat;
-
-/**
- * Contains simple micro-benchmarks.
- */
-@LargeTest
-public class TinyFrameworkBenchmark {
- private static final int MINIMAL_ITERATION = 1000;
- private static final int MEASURE_SECONDS = 1;
-
- private static final DecimalFormat sFormatter = new DecimalFormat("#,###");
-
- private void doBenchmark(String name, Runnable r) {
- // Worm up
- for (int i = 0; i < MINIMAL_ITERATION; i++) {
- r.run();
- }
-
- // Start measuring.
- final long start = System.nanoTime();
- final long end = start + MEASURE_SECONDS * 1_000_000_000L;
-
- double iteration = 0;
- while (System.nanoTime() <= end) {
- for (int i = 0; i < MINIMAL_ITERATION; i++) {
- r.run();
- }
- iteration += MINIMAL_ITERATION;
- }
-
- final long realEnd = System.nanoTime();
-
- System.out.println(String.format("%s\t%s", name,
- sFormatter.format((((double) realEnd - start)) / iteration)));
- }
-
- /**
- * Micro-benchmark for a method without a non-stub caller check.
- */
- @Test
- public void benchNoCallerCheck() {
- doBenchmark("No caller check", TinyFrameworkCallerCheck::getOne_noCheck);
- }
-
- /**
- * Micro-benchmark for a method with a non-stub caller check.
- */
- @Test
- public void benchWithCallerCheck() {
- doBenchmark("With caller check", TinyFrameworkCallerCheck::getOne_withCheck);
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 1692c6e89..68673dc2 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -17,10 +17,9 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.android.hoststubgen.test.tinyframework.R.Nested;
-import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass;
import org.junit.Rule;
import org.junit.Test;
@@ -41,16 +40,12 @@
assertThat(tfc.stub).isEqualTo(1);
}
-// @Test
-// public void testDoesntCompile() {
-// TinyFrameworkClass tfc = new TinyFrameworkClass();
-//
-// tfc.addOneInner(1); // Shouldn't compile.
-// tfc.toBeRemoved("abc"); // Shouldn't compile.
-// tfc.unsupportedMethod(); // Shouldn't compile.
-// int a = tfc.keep; // Shouldn't compile
-// int b = tfc.remove; // Shouldn't compile
-// }
+ @Test
+ public void testRemove() {
+ TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
+ assertThrows(NoSuchMethodError.class, () -> tfc.toBeRemoved("abc"));
+ assertThrows(NoSuchFieldError.class, () -> tfc.remove = 1);
+ }
@Test
public void testIgnore() {
@@ -79,48 +74,12 @@
}
@Test
- public void testVisibleButUsesUnsupportedMethod() {
+ public void testUnsupportedMethod() {
TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
thrown.expect(RuntimeException.class);
thrown.expectMessage("not yet supported");
- tfc.visibleButUsesUnsupportedMethod();
- }
-
- @Test
- public void testNestedClass1() {
- assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1);
- }
-
- @Test
- public void testNestedClass2() {
- assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2);
- }
-
- @Test
- public void testNestedClass3() {
- assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3);
- }
-
- @Test
- public void testNestedClass4() {
- assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4);
- }
-
- @Test
- public void testNestedClass5() {
- assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5);
- }
-
- @Test
- public void testNestedClass6() {
- assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6);
- }
-
- @Test
- public void testNestedClass7() {
- assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get())
- .isEqualTo(7);
+ tfc.unsupportedMethod();
}
@Test
@@ -186,28 +145,28 @@
}
@Test
- public void testSubstituteNativeWithThrow() throws Exception {
- // We can't use TinyFrameworkNative.nativeStillNotSupported() directly in this class,
- // because @Throw implies @Keep (not @Stub), and we currently compile this test
- // against the stub jar (so it won't contain @Throw methods).
- //
- // But the method exists at runtime, so we can use reflections to call it.
- //
- // In the real Ravenwood environment, we don't use HostStubGen's stub jar at all,
- // so it's not a problem.
+ public void testSubstituteNativeWithThrow() {
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage("not yet supported");
- final var clazz = TinyFrameworkNative.class;
- final var method = clazz.getMethod("nativeStillNotSupported");
+ TinyFrameworkNative.nativeStillNotSupported();
+ }
- try {
- method.invoke(null);
+ @Test
+ public void testSubstituteNativeWithKeep() {
+ // We don't want to complicate the test by setting up JNI,
+ // so to test out whether the native method is preserved, we
+ // check whether calling it will throw UnsatisfiedLinkError,
+ // which would only happen on native methods.
+ thrown.expect(UnsatisfiedLinkError.class);
- fail("java.lang.reflect.InvocationTargetException expected");
+ TinyFrameworkNative.nativeStillKeep();
+ }
- } catch (java.lang.reflect.InvocationTargetException e) {
- var inner = e.getCause();
- assertThat(inner.getMessage()).contains("not yet supported");
- }
+ @Test
+ public void testNotNativeRedirect() {
+ TinyFrameworkNative.notNativeStaticRedirected();
+ new TinyFrameworkNative().notNativeRedirected();
}
@Test
@@ -216,12 +175,6 @@
thrown.expectMessage("Outer exception");
TinyFrameworkExceptionTester.testException();
-
- }
-
- @Test
- public void testMethodCallBeforeSuperCall() {
- assertThat(new SubClass(3).value).isEqualTo(3);
}
@Test
@@ -231,7 +184,7 @@
// Having this line before assertThat() will ensure these class are already loaded.
var classes = new Class[]{
TinyFrameworkClassWithInitializerStub.class,
- TinyFrameworkClassAnnotations.class,
+ TinyFrameworkAnnotations.class,
TinyFrameworkForTextPolicy.class,
};
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
new file mode 100644
index 0000000..34c98e9
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.hoststubgen.test.tinyframework;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class TinyFrameworkClassWideAnnotationsTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testSimple() {
+ var tfc = new TinyFrameworkClassWideAnnotations();
+ assertThat(tfc.addOne(1)).isEqualTo(2);
+ assertThat(tfc.keep).isEqualTo(1);
+ }
+
+ @Test
+ public void testRemove() {
+ var tfc = new TinyFrameworkClassWideAnnotations();
+ assertThrows(NoSuchMethodError.class, () -> tfc.toBeRemoved("abc"));
+ assertThrows(NoSuchFieldError.class, () -> tfc.remove = 1);
+ }
+
+ @Test
+ public void testSubstitute() {
+ var tfc = new TinyFrameworkClassWideAnnotations();
+ assertThat(tfc.addTwo(1)).isEqualTo(3);
+ }
+
+ @Test
+ public void testUnsupportedMethod() {
+ var tfc = new TinyFrameworkClassWideAnnotations();
+
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage("not yet supported");
+ tfc.unsupportedMethod();
+ }
+
+ @Test
+ public void testMethodCallBeforeSuperCall() {
+ assertThat(new TinyFrameworkNestedClasses.SubClass(3).value).isEqualTo(3);
+ }
+
+ @Test
+ public void testNestedClass1() {
+ assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1);
+ }
+
+ @Test
+ public void testNestedClass2() {
+ assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2);
+ }
+
+ @Test
+ public void testNestedClass3() {
+ assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3);
+ }
+
+ @Test
+ public void testNestedClass4() {
+ assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4);
+ }
+
+ @Test
+ public void testNestedClass5() {
+ assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5);
+ }
+
+ @Test
+ public void testNestedClass6() {
+ assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6);
+ }
+
+ @Test
+ public void testNestedClass7() {
+ assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get())
+ .isEqualTo(7);
+ }
+
+ @Test
+ public void testNestedClass8() {
+ assertThat(new TinyFrameworkNestedClasses.StaticNestedClass.Double$NestedClass().value)
+ .isEqualTo(8);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java
deleted file mode 100644
index 288c716..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2023 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.hoststubgen.test.tinyframework;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class TinyFrameworkClassWithAnnotTest {
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- @Test
- public void testSimple() {
- TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
- assertThat(tfc.addOne(1)).isEqualTo(2);
- assertThat(tfc.stub).isEqualTo(1);
- }
-
-// @Test
-// public void testDoesntCompile() {
-// TinyFrameworkClassWithAnnot tfc = new TinyFrameworkClassWithAnnot();
-//
-// tfc.addOneInner(1); // Shouldn't compile.
-// tfc.toBeRemoved("abc"); // Shouldn't compile.
-// tfc.unsupportedMethod(); // Shouldn't compile.
-// int a = tfc.keep; // Shouldn't compile
-// int b = tfc.remove; // Shouldn't compile
-// }
-
- @Test
- public void testSubstitute() {
- TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
- assertThat(tfc.addTwo(1)).isEqualTo(3);
- }
-
- @Test
- public void testSubstituteNative() {
- TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
- assertThat(tfc.nativeAddThree(1)).isEqualTo(4);
- }
-
- @Test
- public void testVisibleButUsesUnsupportedMethod() {
- TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
-
- thrown.expect(RuntimeException.class);
- thrown.expectMessage("not yet supported");
- tfc.visibleButUsesUnsupportedMethod();
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
index 6b46c84..5b2795c 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
+++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
@@ -23,18 +23,6 @@
import org.objectweb.asm.Opcodes.ACC_STATIC
class AsmUtilsTest {
- private fun checkGetDirectOuterClassName(input: String, expected: String?) {
- assertThat(getDirectOuterClassName(input)).isEqualTo(expected)
- }
-
- @Test
- fun testGetDirectOuterClassName() {
- checkGetDirectOuterClassName("a", null)
- checkGetDirectOuterClassName("a\$x", "a")
- checkGetDirectOuterClassName("a.b.c\$x", "a.b.c")
- checkGetDirectOuterClassName("a.b.c\$x\$y", "a.b.c\$x")
- }
-
@Test
fun testVisibility() {
fun test(access: Int, expected: Visibility) {
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
index f651514..85b6e80 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
+++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
@@ -72,6 +72,18 @@
}
@Test
+ fun testNestedClass() {
+ val f = ClassFilter.buildFromString("a.b.c\nm.n.o\$p\n", false, "X")
+ assertThat(f.matches("a/b/c")).isEqualTo(true)
+ assertThat(f.matches("a/b/c\$d")).isEqualTo(true)
+ assertThat(f.matches("a/b/c\$d\$e")).isEqualTo(true)
+ assertThat(f.matches("m/n/o")).isEqualTo(false)
+ assertThat(f.matches("m/n/o\$p")).isEqualTo(true)
+ assertThat(f.matches("m/n/o\$p\$r")).isEqualTo(true)
+ assertThat(f.matches("m/n/o\$p\$r\$")).isEqualTo(true)
+ }
+
+ @Test
fun testBadFilter1() {
try {
ClassFilter.buildFromString("""
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
index 24d203f..f5af99e 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
@@ -24,20 +24,31 @@
import org.jetbrains.uast.UMethod
/**
- * Given a UMethod, determine if this method is the entrypoint to an interface
- * generated by AIDL, returning the interface name if so, otherwise returning
- * null
+ * Given a UMethod, determine if this method is the entrypoint to an interface generated by AIDL,
+ * returning the interface name if so, otherwise returning null.
*/
fun getContainingAidlInterface(context: JavaContext, node: UMethod): String? {
+ return containingAidlInterfacePsiClass(context, node)?.name
+}
+
+/**
+ * Given a UMethod, determine if this method is the entrypoint to an interface generated by AIDL,
+ * returning the fully qualified interface name if so, otherwise returning null.
+ */
+fun getContainingAidlInterfaceQualified(context: JavaContext, node: UMethod): String? {
+ return containingAidlInterfacePsiClass(context, node)?.qualifiedName
+}
+
+private fun containingAidlInterfacePsiClass(context: JavaContext, node: UMethod): PsiClass? {
val containingStub = containingStub(context, node) ?: return null
val superMethod = node.findSuperMethods(containingStub)
if (superMethod.isEmpty()) return null
- return containingStub.containingClass?.name
+ return containingStub.containingClass
}
-/* Returns the containing Stub class if any. This is not sufficient to infer
- * that the method itself extends an AIDL generated method. See
- * getContainingAidlInterface for that purpose.
+/**
+ * Returns the containing Stub class if any. This is not sufficient to infer that the method itself
+ * extends an AIDL generated method. See getContainingAidlInterface for that purpose.
*/
fun containingStub(context: JavaContext, node: UMethod?): PsiClass? {
var superClass = node?.containingClass?.superClass
@@ -48,7 +59,7 @@
return null
}
-private fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
+fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
if (psiClass == null) return false
if (psiClass.name != "Stub") return false
if (!context.evaluator.isStatic(psiClass)) return false
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt
new file mode 100644
index 0000000..8777712
--- /dev/null
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt
@@ -0,0 +1,774 @@
+/*
+ * 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.google.android.lint.aidl
+
+/**
+ * The exemptAidlInterfaces set was generated by running ExemptAidlInterfacesGenerator on the
+ * entire source tree. To reproduce the results, run generate-exempt-aidl-interfaces.sh
+ * located in tools/lint/utils.
+ *
+ * TODO: b/363248121 - Use the exemptAidlInterfaces set inside PermissionAnnotationDetector when it
+ * gets migrated to a global lint check.
+ */
+val exemptAidlInterfaces = setOf(
+ "android.accessibilityservice.IAccessibilityServiceConnection",
+ "android.accessibilityservice.IBrailleDisplayConnection",
+ "android.accounts.IAccountAuthenticatorResponse",
+ "android.accounts.IAccountManager",
+ "android.accounts.IAccountManagerResponse",
+ "android.adservices.adid.IAdIdProviderService",
+ "android.adservices.adid.IAdIdService",
+ "android.adservices.adid.IGetAdIdCallback",
+ "android.adservices.adid.IGetAdIdProviderCallback",
+ "android.adservices.adselection.AdSelectionCallback",
+ "android.adservices.adselection.AdSelectionOverrideCallback",
+ "android.adservices.adselection.AdSelectionService",
+ "android.adservices.adselection.GetAdSelectionDataCallback",
+ "android.adservices.adselection.PersistAdSelectionResultCallback",
+ "android.adservices.adselection.ReportImpressionCallback",
+ "android.adservices.adselection.ReportInteractionCallback",
+ "android.adservices.adselection.SetAppInstallAdvertisersCallback",
+ "android.adservices.adselection.UpdateAdCounterHistogramCallback",
+ "android.adservices.appsetid.IAppSetIdProviderService",
+ "android.adservices.appsetid.IAppSetIdService",
+ "android.adservices.appsetid.IGetAppSetIdCallback",
+ "android.adservices.appsetid.IGetAppSetIdProviderCallback",
+ "android.adservices.cobalt.IAdServicesCobaltUploadService",
+ "android.adservices.common.IAdServicesCommonCallback",
+ "android.adservices.common.IAdServicesCommonService",
+ "android.adservices.common.IAdServicesCommonStatesCallback",
+ "android.adservices.common.IEnableAdServicesCallback",
+ "android.adservices.common.IUpdateAdIdCallback",
+ "android.adservices.customaudience.CustomAudienceOverrideCallback",
+ "android.adservices.customaudience.FetchAndJoinCustomAudienceCallback",
+ "android.adservices.customaudience.ICustomAudienceCallback",
+ "android.adservices.customaudience.ICustomAudienceService",
+ "android.adservices.customaudience.ScheduleCustomAudienceUpdateCallback",
+ "android.adservices.extdata.IAdServicesExtDataStorageService",
+ "android.adservices.extdata.IGetAdServicesExtDataCallback",
+ "android.adservices.measurement.IMeasurementApiStatusCallback",
+ "android.adservices.measurement.IMeasurementCallback",
+ "android.adservices.measurement.IMeasurementService",
+ "android.adservices.ondevicepersonalization.aidl.IDataAccessService",
+ "android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback",
+ "android.adservices.ondevicepersonalization.aidl.IExecuteCallback",
+ "android.adservices.ondevicepersonalization.aidl.IFederatedComputeCallback",
+ "android.adservices.ondevicepersonalization.aidl.IFederatedComputeService",
+ "android.adservices.ondevicepersonalization.aidl.IIsolatedModelService",
+ "android.adservices.ondevicepersonalization.aidl.IIsolatedModelServiceCallback",
+ "android.adservices.ondevicepersonalization.aidl.IIsolatedService",
+ "android.adservices.ondevicepersonalization.aidl.IIsolatedServiceCallback",
+ "android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigService",
+ "android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigServiceCallback",
+ "android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationDebugService",
+ "android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService",
+ "android.adservices.ondevicepersonalization.aidl.IRegisterMeasurementEventCallback",
+ "android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback",
+ "android.adservices.shell.IShellCommand",
+ "android.adservices.shell.IShellCommandCallback",
+ "android.adservices.signals.IProtectedSignalsService",
+ "android.adservices.signals.UpdateSignalsCallback",
+ "android.adservices.topics.IGetTopicsCallback",
+ "android.adservices.topics.ITopicsService",
+ "android.app.admin.IDevicePolicyManager",
+ "android.app.adservices.IAdServicesManager",
+ "android.app.ambientcontext.IAmbientContextManager",
+ "android.app.ambientcontext.IAmbientContextObserver",
+ "android.app.appsearch.aidl.IAppFunctionService",
+ "android.app.appsearch.aidl.IAppSearchBatchResultCallback",
+ "android.app.appsearch.aidl.IAppSearchManager",
+ "android.app.appsearch.aidl.IAppSearchObserverProxy",
+ "android.app.appsearch.aidl.IAppSearchResultCallback",
+ "android.app.backup.IBackupCallback",
+ "android.app.backup.IBackupManager",
+ "android.app.backup.IRestoreSession",
+ "android.app.blob.IBlobCommitCallback",
+ "android.app.blob.IBlobStoreManager",
+ "android.app.blob.IBlobStoreSession",
+ "android.app.contentsuggestions.IContentSuggestionsManager",
+ "android.app.contextualsearch.IContextualSearchManager",
+ "android.app.ecm.IEnhancedConfirmationManager",
+ "android.apphibernation.IAppHibernationService",
+ "android.app.IActivityClientController",
+ "android.app.IActivityController",
+ "android.app.IActivityTaskManager",
+ "android.app.IAlarmCompleteListener",
+ "android.app.IAlarmListener",
+ "android.app.IAlarmManager",
+ "android.app.IApplicationThread",
+ "android.app.IAppTask",
+ "android.app.IAppTraceRetriever",
+ "android.app.IAssistDataReceiver",
+ "android.app.IForegroundServiceObserver",
+ "android.app.IGameManagerService",
+ "android.app.IGrammaticalInflectionManager",
+ "android.app.ILocaleManager",
+ "android.app.INotificationManager",
+ "android.app.IParcelFileDescriptorRetriever",
+ "android.app.IProcessObserver",
+ "android.app.ISearchManager",
+ "android.app.IStopUserCallback",
+ "android.app.ITaskStackListener",
+ "android.app.IUiModeManager",
+ "android.app.IUriGrantsManager",
+ "android.app.IUserSwitchObserver",
+ "android.app.IWallpaperManager",
+ "android.app.job.IJobCallback",
+ "android.app.job.IJobScheduler",
+ "android.app.job.IJobService",
+ "android.app.ondeviceintelligence.IDownloadCallback",
+ "android.app.ondeviceintelligence.IFeatureCallback",
+ "android.app.ondeviceintelligence.IFeatureDetailsCallback",
+ "android.app.ondeviceintelligence.IListFeaturesCallback",
+ "android.app.ondeviceintelligence.IOnDeviceIntelligenceManager",
+ "android.app.ondeviceintelligence.IProcessingSignal",
+ "android.app.ondeviceintelligence.IResponseCallback",
+ "android.app.ondeviceintelligence.IStreamingResponseCallback",
+ "android.app.ondeviceintelligence.ITokenInfoCallback",
+ "android.app.people.IPeopleManager",
+ "android.app.pinner.IPinnerService",
+ "android.app.prediction.IPredictionManager",
+ "android.app.role.IOnRoleHoldersChangedListener",
+ "android.app.role.IRoleController",
+ "android.app.role.IRoleManager",
+ "android.app.sdksandbox.ILoadSdkCallback",
+ "android.app.sdksandbox.IRequestSurfacePackageCallback",
+ "android.app.sdksandbox.ISdkSandboxManager",
+ "android.app.sdksandbox.ISdkSandboxProcessDeathCallback",
+ "android.app.sdksandbox.ISdkToServiceCallback",
+ "android.app.sdksandbox.ISharedPreferencesSyncCallback",
+ "android.app.sdksandbox.IUnloadSdkCallback",
+ "android.app.sdksandbox.testutils.testscenario.ISdkSandboxTestExecutor",
+ "android.app.search.ISearchUiManager",
+ "android.app.slice.ISliceManager",
+ "android.app.smartspace.ISmartspaceManager",
+ "android.app.timedetector.ITimeDetectorService",
+ "android.app.timezonedetector.ITimeZoneDetectorService",
+ "android.app.trust.ITrustManager",
+ "android.app.usage.IStorageStatsManager",
+ "android.app.usage.IUsageStatsManager",
+ "android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager",
+ "android.app.wearable.IWearableSensingCallback",
+ "android.app.wearable.IWearableSensingManager",
+ "android.bluetooth.IBluetooth",
+ "android.bluetooth.IBluetoothA2dp",
+ "android.bluetooth.IBluetoothA2dpSink",
+ "android.bluetooth.IBluetoothActivityEnergyInfoListener",
+ "android.bluetooth.IBluetoothAvrcpController",
+ "android.bluetooth.IBluetoothCallback",
+ "android.bluetooth.IBluetoothConnectionCallback",
+ "android.bluetooth.IBluetoothCsipSetCoordinator",
+ "android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback",
+ "android.bluetooth.IBluetoothGatt",
+ "android.bluetooth.IBluetoothGattCallback",
+ "android.bluetooth.IBluetoothGattServerCallback",
+ "android.bluetooth.IBluetoothHapClient",
+ "android.bluetooth.IBluetoothHapClientCallback",
+ "android.bluetooth.IBluetoothHeadset",
+ "android.bluetooth.IBluetoothHeadsetClient",
+ "android.bluetooth.IBluetoothHearingAid",
+ "android.bluetooth.IBluetoothHidDevice",
+ "android.bluetooth.IBluetoothHidDeviceCallback",
+ "android.bluetooth.IBluetoothHidHost",
+ "android.bluetooth.IBluetoothLeAudio",
+ "android.bluetooth.IBluetoothLeAudioCallback",
+ "android.bluetooth.IBluetoothLeBroadcastAssistant",
+ "android.bluetooth.IBluetoothLeBroadcastAssistantCallback",
+ "android.bluetooth.IBluetoothLeBroadcastCallback",
+ "android.bluetooth.IBluetoothLeCallControl",
+ "android.bluetooth.IBluetoothLeCallControlCallback",
+ "android.bluetooth.IBluetoothManager",
+ "android.bluetooth.IBluetoothManagerCallback",
+ "android.bluetooth.IBluetoothMap",
+ "android.bluetooth.IBluetoothMapClient",
+ "android.bluetooth.IBluetoothMcpServiceManager",
+ "android.bluetooth.IBluetoothMetadataListener",
+ "android.bluetooth.IBluetoothOobDataCallback",
+ "android.bluetooth.IBluetoothPan",
+ "android.bluetooth.IBluetoothPanCallback",
+ "android.bluetooth.IBluetoothPbap",
+ "android.bluetooth.IBluetoothPbapClient",
+ "android.bluetooth.IBluetoothPreferredAudioProfilesCallback",
+ "android.bluetooth.IBluetoothQualityReportReadyCallback",
+ "android.bluetooth.IBluetoothSap",
+ "android.bluetooth.IBluetoothScan",
+ "android.bluetooth.IBluetoothSocketManager",
+ "android.bluetooth.IBluetoothVolumeControl",
+ "android.bluetooth.IBluetoothVolumeControlCallback",
+ "android.bluetooth.le.IAdvertisingSetCallback",
+ "android.bluetooth.le.IDistanceMeasurementCallback",
+ "android.bluetooth.le.IPeriodicAdvertisingCallback",
+ "android.bluetooth.le.IScannerCallback",
+ "android.companion.ICompanionDeviceManager",
+ "android.companion.IOnMessageReceivedListener",
+ "android.companion.IOnTransportsChangedListener",
+ "android.companion.virtualcamera.IVirtualCameraCallback",
+ "android.companion.virtual.IVirtualDevice",
+ "android.companion.virtual.IVirtualDeviceManager",
+ "android.companion.virtualnative.IVirtualDeviceManagerNative",
+ "android.content.IClipboard",
+ "android.content.IContentService",
+ "android.content.IIntentReceiver",
+ "android.content.IIntentSender",
+ "android.content.integrity.IAppIntegrityManager",
+ "android.content.IRestrictionsManager",
+ "android.content.ISyncAdapterUnsyncableAccountCallback",
+ "android.content.ISyncContext",
+ "android.content.om.IOverlayManager",
+ "android.content.pm.dex.IArtManager",
+ "android.content.pm.dex.ISnapshotRuntimeProfileCallback",
+ "android.content.pm.IBackgroundInstallControlService",
+ "android.content.pm.ICrossProfileApps",
+ "android.content.pm.IDataLoaderManager",
+ "android.content.pm.IDataLoaderStatusListener",
+ "android.content.pm.ILauncherApps",
+ "android.content.pm.IOnChecksumsReadyListener",
+ "android.content.pm.IOtaDexopt",
+ "android.content.pm.IPackageDataObserver",
+ "android.content.pm.IPackageDeleteObserver",
+ "android.content.pm.IPackageInstaller",
+ "android.content.pm.IPackageInstallerSession",
+ "android.content.pm.IPackageInstallerSessionFileSystemConnector",
+ "android.content.pm.IPackageInstallObserver2",
+ "android.content.pm.IPackageLoadingProgressCallback",
+ "android.content.pm.IPackageManager",
+ "android.content.pm.IPackageManagerNative",
+ "android.content.pm.IPackageMoveObserver",
+ "android.content.pm.IPinItemRequest",
+ "android.content.pm.IShortcutService",
+ "android.content.pm.IStagedApexObserver",
+ "android.content.pm.verify.domain.IDomainVerificationManager",
+ "android.content.res.IResourcesManager",
+ "android.content.rollback.IRollbackManager",
+ "android.credentials.ICredentialManager",
+ "android.debug.IAdbTransport",
+ "android.devicelock.IDeviceLockService",
+ "android.devicelock.IGetDeviceIdCallback",
+ "android.devicelock.IGetKioskAppsCallback",
+ "android.devicelock.IIsDeviceLockedCallback",
+ "android.devicelock.IVoidResultCallback",
+ "android.federatedcompute.aidl.IExampleStoreCallback",
+ "android.federatedcompute.aidl.IExampleStoreIterator",
+ "android.federatedcompute.aidl.IExampleStoreIteratorCallback",
+ "android.federatedcompute.aidl.IExampleStoreService",
+ "android.federatedcompute.aidl.IFederatedComputeCallback",
+ "android.federatedcompute.aidl.IFederatedComputeService",
+ "android.federatedcompute.aidl.IResultHandlingService",
+ "android.flags.IFeatureFlags",
+ "android.frameworks.location.altitude.IAltitudeService",
+ "android.frameworks.vibrator.IVibratorController",
+ "android.frameworks.vibrator.IVibratorControlService",
+ "android.gsi.IGsiServiceCallback",
+ "android.hardware.biometrics.AuthenticationStateListener",
+ "android.hardware.biometrics.common.ICancellationSignal",
+ "android.hardware.biometrics.face.IFace",
+ "android.hardware.biometrics.face.ISession",
+ "android.hardware.biometrics.face.ISessionCallback",
+ "android.hardware.biometrics.fingerprint.IFingerprint",
+ "android.hardware.biometrics.fingerprint.ISession",
+ "android.hardware.biometrics.fingerprint.ISessionCallback",
+ "android.hardware.biometrics.IAuthService",
+ "android.hardware.biometrics.IBiometricAuthenticator",
+ "android.hardware.biometrics.IBiometricContextListener",
+ "android.hardware.biometrics.IBiometricSensorReceiver",
+ "android.hardware.biometrics.IBiometricService",
+ "android.hardware.biometrics.IBiometricStateListener",
+ "android.hardware.biometrics.IBiometricSysuiReceiver",
+ "android.hardware.biometrics.IInvalidationCallback",
+ "android.hardware.biometrics.ITestSession",
+ "android.hardware.broadcastradio.IAnnouncementListener",
+ "android.hardware.broadcastradio.ITunerCallback",
+ "android.hardware.contexthub.IContextHubCallback",
+ "android.hardware.devicestate.IDeviceStateManager",
+ "android.hardware.display.IColorDisplayManager",
+ "android.hardware.display.IDisplayManager",
+ "android.hardware.face.IFaceAuthenticatorsRegisteredCallback",
+ "android.hardware.face.IFaceService",
+ "android.hardware.face.IFaceServiceReceiver",
+ "android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback",
+ "android.hardware.fingerprint.IFingerprintClientActiveCallback",
+ "android.hardware.fingerprint.IFingerprintService",
+ "android.hardware.fingerprint.IFingerprintServiceReceiver",
+ "android.hardware.fingerprint.IUdfpsOverlayControllerCallback",
+ "android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback",
+ "android.hardware.hdmi.IHdmiControlCallback",
+ "android.hardware.hdmi.IHdmiControlService",
+ "android.hardware.hdmi.IHdmiDeviceEventListener",
+ "android.hardware.hdmi.IHdmiHotplugEventListener",
+ "android.hardware.hdmi.IHdmiSystemAudioModeChangeListener",
+ "android.hardware.health.IHealthInfoCallback",
+ "android.hardware.ICameraServiceProxy",
+ "android.hardware.IConsumerIrService",
+ "android.hardware.input.IInputManager",
+ "android.hardware.iris.IIrisService",
+ "android.hardware.ISensorPrivacyManager",
+ "android.hardware.ISerialManager",
+ "android.hardware.lights.ILightsManager",
+ "android.hardware.location.IContextHubClient",
+ "android.hardware.location.IContextHubClientCallback",
+ "android.hardware.location.IContextHubService",
+ "android.hardware.location.IContextHubTransactionCallback",
+ "android.hardware.location.ISignificantPlaceProviderManager",
+ "android.hardware.radio.IAnnouncementListener",
+ "android.hardware.radio.ICloseHandle",
+ "android.hardware.radio.ims.media.IImsMedia",
+ "android.hardware.radio.ims.media.IImsMediaListener",
+ "android.hardware.radio.ims.media.IImsMediaSession",
+ "android.hardware.radio.ims.media.IImsMediaSessionListener",
+ "android.hardware.radio.IRadioService",
+ "android.hardware.radio.ITuner",
+ "android.hardware.radio.sap.ISapCallback",
+ "android.hardware.soundtrigger3.ISoundTriggerHw",
+ "android.hardware.soundtrigger3.ISoundTriggerHwCallback",
+ "android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback",
+ "android.hardware.soundtrigger.IRecognitionStatusCallback",
+ "android.hardware.tetheroffload.ITetheringOffloadCallback",
+ "android.hardware.thermal.IThermalChangedCallback",
+ "android.hardware.tv.hdmi.cec.IHdmiCecCallback",
+ "android.hardware.tv.hdmi.connection.IHdmiConnectionCallback",
+ "android.hardware.tv.hdmi.earc.IEArcCallback",
+ "android.hardware.usb.gadget.IUsbGadgetCallback",
+ "android.hardware.usb.IUsbCallback",
+ "android.hardware.usb.IUsbManager",
+ "android.hardware.usb.IUsbSerialReader",
+ "android.hardware.wifi.hostapd.IHostapdCallback",
+ "android.hardware.wifi.IWifiChipEventCallback",
+ "android.hardware.wifi.IWifiEventCallback",
+ "android.hardware.wifi.IWifiNanIfaceEventCallback",
+ "android.hardware.wifi.IWifiRttControllerEventCallback",
+ "android.hardware.wifi.IWifiStaIfaceEventCallback",
+ "android.hardware.wifi.supplicant.INonStandardCertCallback",
+ "android.hardware.wifi.supplicant.ISupplicantP2pIfaceCallback",
+ "android.hardware.wifi.supplicant.ISupplicantStaIfaceCallback",
+ "android.hardware.wifi.supplicant.ISupplicantStaNetworkCallback",
+ "android.health.connect.aidl.IAccessLogsResponseCallback",
+ "android.health.connect.aidl.IActivityDatesResponseCallback",
+ "android.health.connect.aidl.IAggregateRecordsResponseCallback",
+ "android.health.connect.aidl.IApplicationInfoResponseCallback",
+ "android.health.connect.aidl.IChangeLogsResponseCallback",
+ "android.health.connect.aidl.IDataStagingFinishedCallback",
+ "android.health.connect.aidl.IEmptyResponseCallback",
+ "android.health.connect.aidl.IGetChangeLogTokenCallback",
+ "android.health.connect.aidl.IGetHealthConnectDataStateCallback",
+ "android.health.connect.aidl.IGetHealthConnectMigrationUiStateCallback",
+ "android.health.connect.aidl.IGetPriorityResponseCallback",
+ "android.health.connect.aidl.IHealthConnectService",
+ "android.health.connect.aidl.IInsertRecordsResponseCallback",
+ "android.health.connect.aidl.IMedicalDataSourceResponseCallback",
+ "android.health.connect.aidl.IMedicalResourcesResponseCallback",
+ "android.health.connect.aidl.IMigrationCallback",
+ "android.health.connect.aidl.IReadMedicalResourcesResponseCallback",
+ "android.health.connect.aidl.IReadRecordsResponseCallback",
+ "android.health.connect.aidl.IRecordTypeInfoResponseCallback",
+ "android.health.connect.exportimport.IImportStatusCallback",
+ "android.health.connect.exportimport.IQueryDocumentProvidersCallback",
+ "android.health.connect.exportimport.IScheduledExportStatusCallback",
+ "android.location.ICountryDetector",
+ "android.location.IGpsGeofenceHardware",
+ "android.location.ILocationManager",
+ "android.location.provider.ILocationProviderManager",
+ "android.media.IAudioRoutesObserver",
+ "android.media.IMediaCommunicationService",
+ "android.media.IMediaCommunicationServiceCallback",
+ "android.media.IMediaController2",
+ "android.media.IMediaRoute2ProviderServiceCallback",
+ "android.media.IMediaRouterService",
+ "android.media.IMediaSession2",
+ "android.media.IMediaSession2Service",
+ "android.media.INativeSpatializerCallback",
+ "android.media.IPlaybackConfigDispatcher",
+ "android.media.IRecordingConfigDispatcher",
+ "android.media.IRemoteDisplayCallback",
+ "android.media.ISoundDoseCallback",
+ "android.media.ISpatializerHeadTrackingCallback",
+ "android.media.ITranscodingClientCallback",
+ "android.media.metrics.IMediaMetricsManager",
+ "android.media.midi.IMidiManager",
+ "android.media.musicrecognition.IMusicRecognitionAttributionTagCallback",
+ "android.media.musicrecognition.IMusicRecognitionManager",
+ "android.media.musicrecognition.IMusicRecognitionServiceCallback",
+ "android.media.projection.IMediaProjection",
+ "android.media.projection.IMediaProjectionCallback",
+ "android.media.projection.IMediaProjectionManager",
+ "android.media.projection.IMediaProjectionWatcherCallback",
+ "android.media.session.ISession",
+ "android.media.session.ISessionController",
+ "android.media.session.ISessionManager",
+ "android.media.soundtrigger.ISoundTriggerDetectionServiceClient",
+ "android.media.soundtrigger_middleware.IInjectGlobalEvent",
+ "android.media.soundtrigger_middleware.IInjectModelEvent",
+ "android.media.soundtrigger_middleware.IInjectRecognitionEvent",
+ "android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService",
+ "android.media.soundtrigger_middleware.ISoundTriggerModule",
+ "android.media.tv.ad.ITvAdManager",
+ "android.media.tv.ad.ITvAdSessionCallback",
+ "android.media.tv.interactive.ITvInteractiveAppManager",
+ "android.media.tv.interactive.ITvInteractiveAppServiceCallback",
+ "android.media.tv.interactive.ITvInteractiveAppSessionCallback",
+ "android.media.tv.ITvInputHardware",
+ "android.media.tv.ITvInputManager",
+ "android.media.tv.ITvInputServiceCallback",
+ "android.media.tv.ITvInputSessionCallback",
+ "android.media.tv.ITvRemoteServiceInput",
+ "android.nearby.aidl.IOffloadCallback",
+ "android.nearby.IBroadcastListener",
+ "android.nearby.INearbyManager",
+ "android.nearby.IScanListener",
+ "android.net.connectivity.aidl.ConnectivityNative",
+ "android.net.dhcp.IDhcpEventCallbacks",
+ "android.net.dhcp.IDhcpServer",
+ "android.net.dhcp.IDhcpServerCallbacks",
+ "android.net.ICaptivePortal",
+ "android.net.IConnectivityDiagnosticsCallback",
+ "android.net.IConnectivityManager",
+ "android.net.IEthernetManager",
+ "android.net.IEthernetServiceListener",
+ "android.net.IIntResultListener",
+ "android.net.IIpConnectivityMetrics",
+ "android.net.IIpMemoryStore",
+ "android.net.IIpMemoryStoreCallbacks",
+ "android.net.IIpSecService",
+ "android.net.INetdEventCallback",
+ "android.net.INetdUnsolicitedEventListener",
+ "android.net.INetworkActivityListener",
+ "android.net.INetworkAgent",
+ "android.net.INetworkAgentRegistry",
+ "android.net.INetworkInterfaceOutcomeReceiver",
+ "android.net.INetworkManagementEventObserver",
+ "android.net.INetworkMonitor",
+ "android.net.INetworkMonitorCallbacks",
+ "android.net.INetworkOfferCallback",
+ "android.net.INetworkPolicyListener",
+ "android.net.INetworkPolicyManager",
+ "android.net.INetworkScoreService",
+ "android.net.INetworkStackConnector",
+ "android.net.INetworkStackStatusCallback",
+ "android.net.INetworkStatsService",
+ "android.net.INetworkStatsSession",
+ "android.net.IOnCompleteListener",
+ "android.net.IPacProxyManager",
+ "android.net.ip.IIpClient",
+ "android.net.ip.IIpClientCallbacks",
+ "android.net.ipmemorystore.IOnBlobRetrievedListener",
+ "android.net.ipmemorystore.IOnL2KeyResponseListener",
+ "android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener",
+ "android.net.ipmemorystore.IOnSameL3NetworkResponseListener",
+ "android.net.ipmemorystore.IOnStatusAndCountListener",
+ "android.net.ipmemorystore.IOnStatusListener",
+ "android.net.IQosCallback",
+ "android.net.ISocketKeepaliveCallback",
+ "android.net.ITestNetworkManager",
+ "android.net.ITetheredInterfaceCallback",
+ "android.net.ITetheringConnector",
+ "android.net.ITetheringEventCallback",
+ "android.net.IVpnManager",
+ "android.net.mdns.aidl.IMDnsEventListener",
+ "android.net.metrics.INetdEventListener",
+ "android.net.netstats.IUsageCallback",
+ "android.net.netstats.provider.INetworkStatsProvider",
+ "android.net.netstats.provider.INetworkStatsProviderCallback",
+ "android.net.nsd.INsdManager",
+ "android.net.nsd.INsdManagerCallback",
+ "android.net.nsd.INsdServiceConnector",
+ "android.net.nsd.IOffloadEngine",
+ "android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener",
+ "android.net.thread.IActiveOperationalDatasetReceiver",
+ "android.net.thread.IConfigurationReceiver",
+ "android.net.thread.IOperationalDatasetCallback",
+ "android.net.thread.IOperationReceiver",
+ "android.net.thread.IStateCallback",
+ "android.net.thread.IThreadNetworkController",
+ "android.net.thread.IThreadNetworkManager",
+ "android.net.vcn.IVcnManagementService",
+ "android.net.wear.ICompanionDeviceManagerProxy",
+ "android.net.wifi.aware.IWifiAwareDiscoverySessionCallback",
+ "android.net.wifi.aware.IWifiAwareEventCallback",
+ "android.net.wifi.aware.IWifiAwareMacAddressProvider",
+ "android.net.wifi.aware.IWifiAwareManager",
+ "android.net.wifi.hotspot2.IProvisioningCallback",
+ "android.net.wifi.IActionListener",
+ "android.net.wifi.IBooleanListener",
+ "android.net.wifi.IByteArrayListener",
+ "android.net.wifi.ICoexCallback",
+ "android.net.wifi.IDppCallback",
+ "android.net.wifi.IIntegerListener",
+ "android.net.wifi.IInterfaceCreationInfoCallback",
+ "android.net.wifi.ILastCallerListener",
+ "android.net.wifi.IListListener",
+ "android.net.wifi.ILocalOnlyConnectionStatusListener",
+ "android.net.wifi.ILocalOnlyHotspotCallback",
+ "android.net.wifi.IMacAddressListListener",
+ "android.net.wifi.IMapListener",
+ "android.net.wifi.INetworkRequestMatchCallback",
+ "android.net.wifi.INetworkRequestUserSelectionCallback",
+ "android.net.wifi.IOnWifiActivityEnergyInfoListener",
+ "android.net.wifi.IOnWifiDriverCountryCodeChangedListener",
+ "android.net.wifi.IOnWifiUsabilityStatsListener",
+ "android.net.wifi.IPnoScanResultsCallback",
+ "android.net.wifi.IScanDataListener",
+ "android.net.wifi.IScanResultsCallback",
+ "android.net.wifi.IScoreUpdateObserver",
+ "android.net.wifi.ISoftApCallback",
+ "android.net.wifi.IStringListener",
+ "android.net.wifi.ISubsystemRestartCallback",
+ "android.net.wifi.ISuggestionConnectionStatusListener",
+ "android.net.wifi.ISuggestionUserApprovalStatusListener",
+ "android.net.wifi.ITrafficStateCallback",
+ "android.net.wifi.ITwtCallback",
+ "android.net.wifi.ITwtCapabilitiesListener",
+ "android.net.wifi.ITwtStatsListener",
+ "android.net.wifi.IWifiBandsListener",
+ "android.net.wifi.IWifiConnectedNetworkScorer",
+ "android.net.wifi.IWifiLowLatencyLockListener",
+ "android.net.wifi.IWifiManager",
+ "android.net.wifi.IWifiNetworkSelectionConfigListener",
+ "android.net.wifi.IWifiNetworkStateChangedListener",
+ "android.net.wifi.IWifiScanner",
+ "android.net.wifi.IWifiScannerListener",
+ "android.net.wifi.IWifiVerboseLoggingStatusChangedListener",
+ "android.net.wifi.p2p.IWifiP2pListener",
+ "android.net.wifi.p2p.IWifiP2pManager",
+ "android.net.wifi.rtt.IRttCallback",
+ "android.net.wifi.rtt.IWifiRttManager",
+ "android.ondevicepersonalization.IOnDevicePersonalizationSystemService",
+ "android.ondevicepersonalization.IOnDevicePersonalizationSystemServiceCallback",
+ "android.os.IBatteryPropertiesRegistrar",
+ "android.os.ICancellationSignal",
+ "android.os.IDeviceIdentifiersPolicyService",
+ "android.os.IDeviceIdleController",
+ "android.os.IDumpstate",
+ "android.os.IDumpstateListener",
+ "android.os.IExternalVibratorService",
+ "android.os.IHardwarePropertiesManager",
+ "android.os.IHintManager",
+ "android.os.IHintSession",
+ "android.os.IIncidentCompanion",
+ "android.os.image.IDynamicSystemService",
+ "android.os.incremental.IStorageHealthListener",
+ "android.os.INetworkManagementService",
+ "android.os.IPendingIntentRef",
+ "android.os.IPowerStatsService",
+ "android.os.IProfilingResultCallback",
+ "android.os.IProfilingService",
+ "android.os.IProgressListener",
+ "android.os.IPullAtomCallback",
+ "android.os.IRecoverySystem",
+ "android.os.IRemoteCallback",
+ "android.os.ISecurityStateManager",
+ "android.os.IServiceCallback",
+ "android.os.IStatsCompanionService",
+ "android.os.IStatsManagerService",
+ "android.os.IStatsQueryCallback",
+ "android.os.ISystemConfig",
+ "android.os.ISystemUpdateManager",
+ "android.os.IThermalEventListener",
+ "android.os.IUpdateLock",
+ "android.os.IUserManager",
+ "android.os.IUserRestrictionsListener",
+ "android.os.IVibratorManagerService",
+ "android.os.IVoldListener",
+ "android.os.IVoldMountCallback",
+ "android.os.IVoldTaskListener",
+ "android.os.logcat.ILogcatManagerService",
+ "android.permission.ILegacyPermissionManager",
+ "android.permission.IPermissionChecker",
+ "android.permission.IPermissionManager",
+ "android.print.IPrintManager",
+ "android.print.IPrintSpoolerCallbacks",
+ "android.print.IPrintSpoolerClient",
+ "android.printservice.IPrintServiceClient",
+ "android.printservice.recommendation.IRecommendationServiceCallbacks",
+ "android.provider.aidl.IDeviceConfigManager",
+ "android.remoteauth.IDeviceDiscoveryListener",
+ "android.safetycenter.IOnSafetyCenterDataChangedListener",
+ "android.safetycenter.ISafetyCenterManager",
+ "android.scheduling.IRebootReadinessManager",
+ "android.scheduling.IRequestRebootReadinessStatusListener",
+ "android.security.attestationverification.IAttestationVerificationManagerService",
+ "android.security.IFileIntegrityService",
+ "android.security.keystore.IKeyAttestationApplicationIdProvider",
+ "android.security.rkp.IRegistration",
+ "android.security.rkp.IRemoteProvisioning",
+ "android.service.appprediction.IPredictionService",
+ "android.service.assist.classification.IFieldClassificationCallback",
+ "android.service.attention.IAttentionCallback",
+ "android.service.attention.IProximityUpdateCallback",
+ "android.service.autofill.augmented.IFillCallback",
+ "android.service.autofill.IConvertCredentialCallback",
+ "android.service.autofill.IFillCallback",
+ "android.service.autofill.IInlineSuggestionUiCallback",
+ "android.service.autofill.ISaveCallback",
+ "android.service.autofill.ISurfacePackageResultCallback",
+ "android.service.contentcapture.IContentCaptureServiceCallback",
+ "android.service.contentcapture.IContentProtectionAllowlistCallback",
+ "android.service.contentcapture.IDataShareCallback",
+ "android.service.credentials.IBeginCreateCredentialCallback",
+ "android.service.credentials.IBeginGetCredentialCallback",
+ "android.service.credentials.IClearCredentialStateCallback",
+ "android.service.dreams.IDreamManager",
+ "android.service.games.IGameServiceController",
+ "android.service.games.IGameSessionController",
+ "android.service.notification.IStatusBarNotificationHolder",
+ "android.service.oemlock.IOemLockService",
+ "android.service.ondeviceintelligence.IProcessingUpdateStatusCallback",
+ "android.service.ondeviceintelligence.IRemoteProcessingService",
+ "android.service.ondeviceintelligence.IRemoteStorageService",
+ "android.service.persistentdata.IPersistentDataBlockService",
+ "android.service.resolver.IResolverRankerResult",
+ "android.service.rotationresolver.IRotationResolverCallback",
+ "android.service.textclassifier.ITextClassifierCallback",
+ "android.service.textclassifier.ITextClassifierService",
+ "android.service.timezone.ITimeZoneProviderManager",
+ "android.service.trust.ITrustAgentServiceCallback",
+ "android.service.voice.IDetectorSessionStorageService",
+ "android.service.voice.IDetectorSessionVisualQueryDetectionCallback",
+ "android.service.voice.IDspHotwordDetectionCallback",
+ "android.service.wallpaper.IWallpaperConnection",
+ "android.speech.IRecognitionListener",
+ "android.speech.IRecognitionService",
+ "android.speech.IRecognitionServiceManager",
+ "android.speech.tts.ITextToSpeechManager",
+ "android.speech.tts.ITextToSpeechSession",
+ "android.system.composd.ICompilationTaskCallback",
+ "android.system.virtualizationmaintenance.IVirtualizationReconciliationCallback",
+ "android.system.virtualizationservice.IVirtualMachineCallback",
+ "android.system.vmtethering.IVmTethering",
+ "android.telephony.imsmedia.IImsAudioSession",
+ "android.telephony.imsmedia.IImsAudioSessionCallback",
+ "android.telephony.imsmedia.IImsMedia",
+ "android.telephony.imsmedia.IImsMediaCallback",
+ "android.telephony.imsmedia.IImsTextSession",
+ "android.telephony.imsmedia.IImsTextSessionCallback",
+ "android.telephony.imsmedia.IImsVideoSession",
+ "android.telephony.imsmedia.IImsVideoSessionCallback",
+ "android.tracing.ITracingServiceProxy",
+ "android.uwb.IOnUwbActivityEnergyInfoListener",
+ "android.uwb.IUwbAdapter",
+ "android.uwb.IUwbAdapterStateCallbacks",
+ "android.uwb.IUwbAdfProvisionStateCallbacks",
+ "android.uwb.IUwbOemExtensionCallback",
+ "android.uwb.IUwbRangingCallbacks",
+ "android.uwb.IUwbVendorUciCallback",
+ "android.view.accessibility.IAccessibilityInteractionConnectionCallback",
+ "android.view.accessibility.IAccessibilityManager",
+ "android.view.accessibility.IMagnificationConnectionCallback",
+ "android.view.accessibility.IRemoteMagnificationAnimationCallback",
+ "android.view.autofill.IAutoFillManager",
+ "android.view.autofill.IAutofillWindowPresenter",
+ "android.view.contentcapture.IContentCaptureManager",
+ "android.view.IDisplayChangeWindowCallback",
+ "android.view.IDisplayWindowListener",
+ "android.view.IInputFilter",
+ "android.view.IInputFilterHost",
+ "android.view.IInputMonitorHost",
+ "android.view.IRecentsAnimationController",
+ "android.view.IRemoteAnimationFinishedCallback",
+ "android.view.ISensitiveContentProtectionManager",
+ "android.view.IWindowId",
+ "android.view.IWindowManager",
+ "android.view.IWindowSession",
+ "android.view.translation.ITranslationManager",
+ "android.view.translation.ITranslationServiceCallback",
+ "android.webkit.IWebViewUpdateService",
+ "android.window.IBackAnimationFinishedCallback",
+ "android.window.IDisplayAreaOrganizerController",
+ "android.window.ITaskFragmentOrganizerController",
+ "android.window.ITaskOrganizerController",
+ "android.window.ITransitionMetricsReporter",
+ "android.window.IUnhandledDragCallback",
+ "android.window.IWindowContainerToken",
+ "android.window.IWindowlessStartingSurfaceCallback",
+ "android.window.IWindowOrganizerController",
+ "androidx.core.uwb.backend.IUwb",
+ "androidx.core.uwb.backend.IUwbClient",
+ "com.android.clockwork.modes.IModeManager",
+ "com.android.clockwork.modes.IStateChangeListener",
+ "com.android.clockwork.power.IWearPowerService",
+ "com.android.devicelockcontroller.IDeviceLockControllerService",
+ "com.android.devicelockcontroller.storage.IGlobalParametersService",
+ "com.android.devicelockcontroller.storage.ISetupParametersService",
+ "com.android.federatedcompute.services.training.aidl.IIsolatedTrainingService",
+ "com.android.federatedcompute.services.training.aidl.ITrainingResultCallback",
+ "com.android.internal.app.IAppOpsActiveCallback",
+ "com.android.internal.app.ILogAccessDialogCallback",
+ "com.android.internal.app.ISoundTriggerService",
+ "com.android.internal.app.ISoundTriggerSession",
+ "com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener",
+ "com.android.internal.app.IVoiceInteractionManagerService",
+ "com.android.internal.app.IVoiceInteractionSessionListener",
+ "com.android.internal.app.IVoiceInteractionSessionShowCallback",
+ "com.android.internal.app.IVoiceInteractionSoundTriggerSession",
+ "com.android.internal.app.procstats.IProcessStats",
+ "com.android.internal.appwidget.IAppWidgetService",
+ "com.android.internal.backup.ITransportStatusCallback",
+ "com.android.internal.compat.IOverrideValidator",
+ "com.android.internal.compat.IPlatformCompat",
+ "com.android.internal.compat.IPlatformCompatNative",
+ "com.android.internal.graphics.fonts.IFontManager",
+ "com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback",
+ "com.android.internal.inputmethod.IConnectionlessHandwritingCallback",
+ "com.android.internal.inputmethod.IImeTracker",
+ "com.android.internal.inputmethod.IInlineSuggestionsRequestCallback",
+ "com.android.internal.inputmethod.IInputContentUriToken",
+ "com.android.internal.inputmethod.IInputMethodPrivilegedOperations",
+ "com.android.internal.inputmethod.IInputMethodSessionCallback",
+ "com.android.internal.net.INetworkWatchlistManager",
+ "com.android.internal.os.IBinaryTransparencyService",
+ "com.android.internal.os.IDropBoxManagerService",
+ "com.android.internal.policy.IKeyguardDismissCallback",
+ "com.android.internal.policy.IKeyguardDrawnCallback",
+ "com.android.internal.policy.IKeyguardExitCallback",
+ "com.android.internal.policy.IKeyguardStateCallback",
+ "com.android.internal.statusbar.IAddTileResultCallback",
+ "com.android.internal.statusbar.ISessionListener",
+ "com.android.internal.statusbar.IStatusBarService",
+ "com.android.internal.telecom.IDeviceIdleControllerAdapter",
+ "com.android.internal.telecom.IInternalServiceRetriever",
+ "com.android.internal.telephony.IMms",
+ "com.android.internal.telephony.ITelephonyRegistry",
+ "com.android.internal.textservice.ISpellCheckerServiceCallback",
+ "com.android.internal.textservice.ITextServicesManager",
+ "com.android.internal.view.IDragAndDropPermissions",
+ "com.android.internal.view.IInputMethodManager",
+ "com.android.internal.view.inline.IInlineContentProvider",
+ "com.android.internal.widget.ILockSettings",
+ "com.android.net.IProxyPortListener",
+ "com.android.net.module.util.IRoutingCoordinator",
+ "com.android.ondevicepersonalization.libraries.plugin.internal.IPluginCallback",
+ "com.android.ondevicepersonalization.libraries.plugin.internal.IPluginExecutorService",
+ "com.android.ondevicepersonalization.libraries.plugin.internal.IPluginStateCallback",
+ "com.android.rkpdapp.IGetKeyCallback",
+ "com.android.rkpdapp.IGetRegistrationCallback",
+ "com.android.rkpdapp.IRegistration",
+ "com.android.rkpdapp.IRemoteProvisioning",
+ "com.android.rkpdapp.IStoreUpgradedKeyCallback",
+ "com.android.sdksandbox.IComputeSdkStorageCallback",
+ "com.android.sdksandbox.ILoadSdkInSandboxCallback",
+ "com.android.sdksandbox.IRequestSurfacePackageFromSdkCallback",
+ "com.android.sdksandbox.ISdkSandboxManagerToSdkSandboxCallback",
+ "com.android.sdksandbox.ISdkSandboxService",
+ "com.android.sdksandbox.IUnloadSdkInSandboxCallback",
+ "com.android.server.profcollect.IProviderStatusCallback",
+ "com.android.server.thread.openthread.IChannelMasksReceiver",
+ "com.android.server.thread.openthread.INsdPublisher",
+ "com.android.server.thread.openthread.IOtDaemonCallback",
+ "com.android.server.thread.openthread.IOtStatusReceiver",
+ "com.google.android.clockwork.ambient.offload.IDisplayOffloadService",
+ "com.google.android.clockwork.ambient.offload.IDisplayOffloadTransitionFinishedCallbacks",
+ "com.google.android.clockwork.healthservices.IHealthService",
+ "vendor.google_clockwork.healthservices.IHealthServicesCallback",
+)
diff --git a/tools/lint/utils/README.md b/tools/lint/utils/README.md
new file mode 100644
index 0000000..b5583c5
--- /dev/null
+++ b/tools/lint/utils/README.md
@@ -0,0 +1,11 @@
+# Utility Android Lint Checks for AOSP
+
+This directory contains scripts that execute utility Android Lint Checks for AOSP, specifically:
+* `enforce_permission_counter.py`: Provides statistics regarding the percentage of annotated/not
+ annotated `AIDL` methods with `@EnforcePermission` annotations.
+* `generate-exempt-aidl-interfaces.sh`: Provides a list of all `AIDL` interfaces in the entire
+ source tree.
+
+When adding a new utility Android Lint check to this directory, consider adding any utility or
+data processing tool you might require. Make sure that your contribution is documented in this
+README file.
diff --git a/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt b/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt
index fa61c42..9842881 100644
--- a/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt
+++ b/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt
@@ -19,6 +19,7 @@
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.android.lint.aidl.ExemptAidlInterfacesGenerator
import com.google.android.lint.aidl.AnnotatedAidlCounter
import com.google.auto.service.AutoService
@@ -27,6 +28,7 @@
class AndroidUtilsIssueRegistry : IssueRegistry() {
override val issues = listOf(
AnnotatedAidlCounter.ISSUE_ANNOTATED_AIDL_COUNTER,
+ ExemptAidlInterfacesGenerator.ISSUE_PERMISSION_ANNOTATION_EXEMPT_AIDL_INTERFACES,
)
override val api: Int
@@ -38,6 +40,6 @@
override val vendor: Vendor = Vendor(
vendorName = "Android",
feedbackUrl = "http://b/issues/new?component=315013",
- contact = "[email protected]"
+ contact = "[email protected]"
)
}
diff --git a/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfacesGenerator.kt b/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfacesGenerator.kt
new file mode 100644
index 0000000..6ad223c
--- /dev/null
+++ b/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfacesGenerator.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Context
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UMethod
+
+/**
+ * Generates a set of fully qualified AIDL Interface names present in the entire source tree with
+ * the following requirement: their implementations have to be inside directories whose path
+ * prefixes match `systemServicePathPrefixes`.
+ */
+class ExemptAidlInterfacesGenerator : AidlImplementationDetector() {
+ private val targetExemptAidlInterfaceNames = mutableSetOf<String>()
+ private val systemServicePathPrefixes = setOf(
+ "frameworks/base/services",
+ "frameworks/base/apex",
+ "frameworks/opt/wear",
+ "packages/modules"
+ )
+
+ // We could've improved performance by visiting classes rather than methods, however, this lint
+ // check won't be run regularly, hence we've decided not to add extra overrides to
+ // AidlImplementationDetector.
+ override fun visitAidlMethod(
+ context: JavaContext,
+ node: UMethod,
+ interfaceName: String,
+ body: UBlockExpression
+ ) {
+ val filePath = context.file.path
+
+ // We perform `filePath.contains` instead of `filePath.startsWith` since getting the
+ // relative path of a source file is non-trivial. That is because `context.file.path`
+ // returns the path to where soong builds the file (i.e. /out/soong/...). Moreover, the
+ // logic to extract the relative path would need to consider several /out/soong/...
+ // locations patterns.
+ if (systemServicePathPrefixes.none { filePath.contains(it) }) return
+
+ val fullyQualifiedInterfaceName =
+ getContainingAidlInterfaceQualified(context, node) ?: return
+
+ targetExemptAidlInterfaceNames.add("\"$fullyQualifiedInterfaceName\",")
+ }
+
+ override fun afterCheckEachProject(context: Context) {
+ if (targetExemptAidlInterfaceNames.isEmpty()) return
+
+ val message = targetExemptAidlInterfaceNames.joinToString("\n")
+
+ context.report(
+ ISSUE_PERMISSION_ANNOTATION_EXEMPT_AIDL_INTERFACES,
+ context.getLocation(context.project.dir),
+ "\n" + message + "\n",
+ )
+ }
+
+ companion object {
+ @JvmField
+ val ISSUE_PERMISSION_ANNOTATION_EXEMPT_AIDL_INTERFACES = Issue.create(
+ id = "PermissionAnnotationExemptAidlInterfaces",
+ briefDescription = "Returns a set of all AIDL interfaces",
+ explanation = """
+ Produces the exemptAidlInterfaces set used by PermissionAnnotationDetector
+ """.trimIndent(),
+ category = Category.SECURITY,
+ priority = 5,
+ severity = Severity.INFORMATIONAL,
+ implementation = Implementation(
+ ExemptAidlInterfacesGenerator::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+ }
+}
diff --git a/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/ExemptAidlInterfacesGeneratorTest.kt b/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/ExemptAidlInterfacesGeneratorTest.kt
new file mode 100644
index 0000000..9a17bb4
--- /dev/null
+++ b/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/ExemptAidlInterfacesGeneratorTest.kt
@@ -0,0 +1,191 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+class ExemptAidlInterfacesGeneratorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = ExemptAidlInterfacesGenerator()
+
+ override fun getIssues(): List<Issue> = listOf(
+ ExemptAidlInterfacesGenerator.ISSUE_PERMISSION_ANNOTATION_EXEMPT_AIDL_INTERFACES,
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ fun testMultipleAidlInterfacesImplemented() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("TestClass1.java"),
+ """
+ package com.android.server;
+ public class TestClass1 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """
+ )
+ .indented(),
+ java(
+ createVisitedPath("TestClass2.java"),
+ """
+ package com.android.server;
+ public class TestClass2 extends IBar.Stub {
+ public void testMethod() {}
+ }
+ """
+ )
+ .indented(),
+ *stubs,
+ )
+ .run()
+ .expect(
+ """
+ app: Information: "IFoo",
+ "IBar", [PermissionAnnotationExemptAidlInterfaces]
+ 0 errors, 0 warnings
+ """
+ )
+ }
+
+ fun testSingleAidlInterfaceRepeated() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("TestClass1.java"),
+ """
+ package com.android.server;
+ public class TestClass1 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """
+ )
+ .indented(),
+ java(
+ createVisitedPath("TestClass2.java"),
+ """
+ package com.android.server;
+ public class TestClass2 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """
+ )
+ .indented(),
+ *stubs,
+ )
+ .run()
+ .expect(
+ """
+ app: Information: "IFoo", [PermissionAnnotationExemptAidlInterfaces]
+ 0 errors, 0 warnings
+ """
+ )
+ }
+
+ fun testAnonymousClassExtendsAidlStub() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("TestClass.java"),
+ """
+ package com.android.server;
+ public class TestClass {
+ private IBinder aidlImpl = new IFoo.Stub() {
+ public void testMethod() {}
+ };
+ }
+ """
+ )
+ .indented(),
+ *stubs,
+ )
+ .run()
+ .expect(
+ """
+ app: Information: "IFoo", [PermissionAnnotationExemptAidlInterfaces]
+ 0 errors, 0 warnings
+ """
+ )
+ }
+
+ fun testNoAidlInterfacesImplemented() {
+ lint()
+ .files(
+ java(
+ createVisitedPath("TestClass.java"),
+ """
+ package com.android.server;
+ public class TestClass {
+ public void testMethod() {}
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testAidlInterfaceImplementedInIgnoredDirectory() {
+ lint()
+ .files(
+ java(
+ ignoredPath,
+ """
+ package com.android.server;
+ public class TestClass1 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """
+ )
+ .indented(),
+ *stubs,
+ )
+ .run()
+ .expectClean()
+ }
+
+ private val interfaceIFoo: TestFile = java(
+ """
+ public interface IFoo extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IFoo {}
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val interfaceIBar: TestFile = java(
+ """
+ public interface IBar extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IBar {}
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(interfaceIFoo, interfaceIBar)
+
+ private fun createVisitedPath(filename: String) =
+ "src/frameworks/base/services/java/com/android/server/$filename"
+
+ private val ignoredPath = "src/test/pkg/TestClass.java"
+}
diff --git a/tools/lint/utils/generate-exempt-aidl-interfaces.sh b/tools/lint/utils/generate-exempt-aidl-interfaces.sh
new file mode 100755
index 0000000..44dcdd7
--- /dev/null
+++ b/tools/lint/utils/generate-exempt-aidl-interfaces.sh
@@ -0,0 +1,59 @@
+#
+# 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.
+#
+
+# Create a directory for the results and a nested temporary directory.
+mkdir -p $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/tmp
+
+# Create a copy of `AndroidGlobalLintChecker.jar` to restore it afterwards.
+cp $ANDROID_BUILD_TOP/prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar \
+ $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/AndroidGlobalLintChecker.jar
+
+# Configure the environment variable required for running the lint check on the entire source tree.
+export ANDROID_LINT_CHECK=PermissionAnnotationExemptAidlInterfaces
+
+# Build the target corresponding to the lint checks present in the `utils` directory.
+m AndroidUtilsLintChecker
+
+# Replace `AndroidGlobalLintChecker.jar` with the newly built `jar` file.
+cp $ANDROID_BUILD_TOP/out/host/linux-x86/framework/AndroidUtilsLintChecker.jar \
+ $ANDROID_BUILD_TOP/prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar;
+
+# Run the lint check on the entire source tree.
+m lint-check
+
+# Copy the archive containing the results of `lint-check` into the temporary directory.
+cp $ANDROID_BUILD_TOP/out/soong/lint-report-text.zip \
+ $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/tmp
+
+cd $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/tmp
+
+# Unzip the archive containing the results of `lint-check`.
+unzip lint-report-text.zip
+
+# Concatenate the results of `lint-check` into a single string.
+concatenated_reports=$(find . -type f | xargs cat)
+
+# Extract the fully qualified names of the AIDL Interfaces from the concatenated results. Output
+# this list into `out/soong/exempt_aidl_interfaces_generator_output/exempt_aidl_interfaces`.
+echo $concatenated_reports | grep -Eo '\"([a-zA-Z0-9_]*\.)+[a-zA-Z0-9_]*\",' | sort | uniq > ../exempt_aidl_interfaces
+
+# Remove the temporary directory.
+rm -rf $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/tmp
+
+# Restore the original copy of `AndroidGlobalLintChecker.jar` and delete the copy.
+cp $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/AndroidGlobalLintChecker.jar \
+ $ANDROID_BUILD_TOP/prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar
+rm $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/AndroidGlobalLintChecker.jar
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
index 0115339..245e802 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
@@ -53,7 +53,7 @@
.setMessageId(key)
.setMessage(log.messageString)
.setLevel(
- ProtoLogLevel.forNumber(log.logLevel.ordinal + 1))
+ ProtoLogLevel.forNumber(log.logLevel.id))
.setGroupId(groupId)
.setLocation(log.position)
)